pax_global_header00006660000000000000000000000064146604743730014530gustar00rootroot0000000000000052 comment=ee6df326e0ec3d6f5f8e103670dead04adebe9af mm3d-1.3.15/000077500000000000000000000000001466047437300124575ustar00rootroot00000000000000mm3d-1.3.15/.github/000077500000000000000000000000001466047437300140175ustar00rootroot00000000000000mm3d-1.3.15/.github/FUNDING.yml000066400000000000000000000000451466047437300156330ustar00rootroot00000000000000github: zturtleman ko_fi: zturtleman mm3d-1.3.15/.github/workflows/000077500000000000000000000000001466047437300160545ustar00rootroot00000000000000mm3d-1.3.15/.github/workflows/build.yml000066400000000000000000000150061466047437300177000ustar00rootroot00000000000000name: Build on: [push, pull_request] jobs: linux-configure-gcc: name: GCC (Linux) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Dependencies run: | sudo apt update sudo apt install autoconf automake make gcc g++ qtbase5-dev qtbase5-dev-tools qttools5-dev-tools libgl1-mesa-dev - name: autogen.sh run: ./autogen.sh - name: configure run: CC=gcc CXX=g++ ./configure - name: make run: make - name: make install run: sudo make install linux-configure-clang: name: Clang (Linux) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Dependencies run: | sudo apt update sudo apt install autoconf automake make gcc g++ qtbase5-dev qtbase5-dev-tools qttools5-dev-tools libgl1-mesa-dev - name: autogen.sh run: ./autogen.sh - name: configure run: CC=clang CXX=clang++ ./configure - name: make run: make - name: make install run: sudo make install linux-make: name: Makefile.generic (Linux, GCC) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Dependencies run: | sudo apt update sudo apt install autoconf automake make gcc g++ qtbase5-dev qtbase5-dev-tools qttools5-dev-tools libgl1-mesa-dev - name: Setup run: cp config.h.generic config.h - name: make run: make -f Makefile.generic - name: make install run: sudo make -f Makefile.generic install linux-flatpak: name: Flatpak (Linux, GCC) # flatpak-builder fails to download glu on ubuntu-22.04 # (glu specified in mm3d-flatpak.yml) # see https://github.com/flatpak/flatpak-builder/issues/468 runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # This is needed for "git describe" to work. - name: Install Dependencies run: | sudo apt update sudo apt install flatpak flatpak-builder flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - name: Set version for the Flatpak run: | # if not a tag, add version if [ $(git describe) != $(git describe --abbrev=0) ]; then sed -i "s||\n |" desktop/moe.clover.mm3d.metainfo.xml fi cat desktop/moe.clover.mm3d.metainfo.xml - name: flatpak-builder run: flatpak-builder --user --install-deps-from=flathub --repo=flatpak-repo --state-dir=flatpak-state flatpak-build mm3d-flatpak.yaml - name: flatpak build-bundle run: flatpak build-bundle flatpak-repo moe.clover.mm3d-$(git describe|sed 's/^v\([0-9]\)/\1/').flatpak moe.clover.mm3d - name: flatpak install run: | flatpak install --user -y moe.clover.mm3d-$(git describe|sed 's/^v\([0-9]\)/\1/').flatpak flatpak list - uses: actions/upload-artifact@v4 with: name: Flatpak Bundle path: ./*.flatpak macOS: name: macOS (x86_64) runs-on: macOS-13 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # This is needed for "git describe" to work. - name: Install Dependencies run: | brew install autoconf automake qt@5 - name: Configure run: | qt_version=$(ls -1 /usr/local/Cellar/qt@5 | sort -rV | head -1) # Get version from line "QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13" macosx_version_min=$(grep QMAKE_MACOSX_DEPLOYMENT_TARGET /usr/local/Cellar/qt@5/${qt_version}/mkspecs/common/macx.conf | cut -d' ' -f3) echo "Qt version: ${qt_version}, minimum macOS version: ${macosx_version_min}" if [ -z "${qt_version}" -o -z "${macosx_version_min}" ]; then exit 1 fi ./autogen.sh ./configure --with-Qt-dir=/usr/local/Cellar/qt@5/${qt_version} --with-macosx-version-min=${macosx_version_min} - name: make run: make - name: make appbundle run: make appbundle - name: Create DMG run: | mm3d_version=$(git describe|sed 's/^v\([0-9]\)/\1/') mm3d_longname="Maverick Model 3D ${mm3d_version}" # Sign .app rmdir "Maverick Model 3D.app/Contents/PlugIns/mm3d/1.3" codesign --deep --force --sign - "Maverick Model 3D.app" mkdir "Maverick Model 3D.app/Contents/PlugIns/mm3d/1.3" # Create .dmg mkdir "${mm3d_longname}" mv "Maverick Model 3D.app" "${mm3d_longname}/." hdiutil create -fs HFS+ -srcfolder "${mm3d_longname}" mm3d-${mm3d_version}.dmg # Sign .dmg codesign -s - mm3d-${mm3d_version}.dmg - uses: actions/upload-artifact@v4 with: name: MacOS App (x86_64) path: ./*.dmg macOS_arm64: name: macOS (ARM64) runs-on: macOS-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # This is needed for "git describe" to work. - name: Install Dependencies run: | brew install autoconf automake qt@5 - name: Configure run: | qt_version=$(ls -1 /opt/homebrew/Cellar/qt@5 | sort -rV | head -1) # Get version from line "QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13" macosx_version_min=$(grep QMAKE_MACOSX_DEPLOYMENT_TARGET /opt/homebrew/Cellar/qt@5/${qt_version}/mkspecs/common/macx.conf | cut -d' ' -f3) echo "Qt version: ${qt_version}, minimum macOS version: ${macosx_version_min}" if [ -z "${qt_version}" -o -z "${macosx_version_min}" ]; then exit 1 fi ./autogen.sh ./configure --with-Qt-dir=/opt/homebrew/Cellar/qt@5/${qt_version} --with-macosx-version-min=${macosx_version_min} - name: make run: make - name: make appbundle run: make appbundle - name: Create DMG run: | mm3d_version=$(git describe|sed 's/^v\([0-9]\)/\1/') mm3d_longname="Maverick Model 3D ${mm3d_version}" # Sign .app rmdir "Maverick Model 3D.app/Contents/PlugIns/mm3d/1.3" codesign --deep --force --sign - "Maverick Model 3D.app" mkdir "Maverick Model 3D.app/Contents/PlugIns/mm3d/1.3" # Create .dmg mkdir "${mm3d_longname}" mv "Maverick Model 3D.app" "${mm3d_longname}/." hdiutil create -fs HFS+ -srcfolder "${mm3d_longname}" mm3d-${mm3d_version}.dmg # Sign .dmg codesign -s - mm3d-${mm3d_version}.dmg - uses: actions/upload-artifact@v4 with: name: MacOS App (ARM64) path: ./*.dmg mm3d-1.3.15/.gitignore000066400000000000000000000001051466047437300144430ustar00rootroot00000000000000Makefile Makefile.in *.o *.a *.qm *.moc.* *.base.* .deps .*.swp tags mm3d-1.3.15/AUTHORS000066400000000000000000000006741466047437300135360ustar00rootroot00000000000000Primary development: Kevin Worcester http://www.misfitcode.com/misfitmodel3d/contact.html Texture reload and Snap Together command: Johannes Kroll Various bug fixes and feature enhancements: Ainsley Pereira Initial Win32 Port Patch: Georg Hennig http://www.hennigbuam.de/georg/ Initial MD3 Filter: Russell Valentine http://coldstonelabs.org/ Maverick Model 3D maintainer: Zack Middleton http://clover.moe/ mm3d-1.3.15/CODE000066400000000000000000000031371466047437300131200ustar00rootroot00000000000000Quick guide to the MM3D code. Directories: src -- All source code libmm3d -- Low-level 3D functionality, model/texture handling mm3dcore -- Core application code depui -- Custom UI widgets for 3D drawing, used by many UI elements qtui -- User interface files (Qt's .ui files) implui -- Code that implements the user interface functionality commands -- Geometry menu commands tools -- Toolbar tools pixmap -- Icons used by mm3d doc -- Online documentation, HTML format translations -- UI translation files man -- Manpage desktop -- Desktop icons and config files util -- Utilities for building plugins -- Place-holder for unpacking and building plugins In general, the most interesting code is in src/libmm3d, src/tools, and src/commands for model manipulation and src/implui for user interface. About half of the code is in src/libmm3d, and about half of the code in src/libmm3d is in the model class (model.h, model.cc, model_*.cc). You can use src/maketags.sh to make ctags files for the source tree. Some development options for ./configure: --enable-debug=[yes/no/cov] The yes and no options turn debug information on and off. The cov option turns on debugging and coverage information. --enable-profile=[yes/no/core] The yes and no options turn profiling information on and off. The core option turns on profiling just for the application libraries (not the User Interface). mm3d-1.3.15/COPYING000066400000000000000000000436151466047437300135230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mm3d-1.3.15/ChangeLog000066400000000000000000000606661466047437300142470ustar00rootroot00000000000000Maverick Model 3D releases published by Zack Middleton. Changes for 1.3.15 (2024-08-18) This adds Quake 3 player export to IQE format, fixes Linux Dark Mode and issues on 2023 Flatpak runtime, and fixes not updating frame count in animation mode until focus changes. General Add dark mode color scheme for model background Add frames, FPS, and loop to New Animation window Add Edit to animation mode for changing the same fields as New Animation window Make Animation Sets window use New Animation dialog Replace Rename in Animation Sets window with Edit for name, frames, FPS, and loop Rename texture to material in Edit Groups window Change max frames for an animation to 9999 (previously 999 but convert to frame animation was limited to 99) Fix not updating frame count in animation mode until focus changes, text editing is disabled now Fix 'paste animation frame' without selecting an animation Fix misplaced "fi"s for configure Fix ignoring user CXXFLAGS in configure Fix compiling on GNU Hurd Documentation Add dark mode support for web browsers Fix viewing in-app help in dark mode Model Formats iqe Add Quake 3 player export for IQE GNU/Linux Add changelog to Linux appstream metadata Update Flatpak runtime to org.kde.Platform 5.15-23.08 Don't use Wayland unless requested (environment variable QT_QPA_PLATFORM=wayland) Fix finding Qt translations in Flatpak macOS Fix configure failure on newer macOS Fix installing Qt translation in the appbundle Changes for 1.3.14 (2023-04-23) General Enable Qt HiDPI support by default and fix a few layout issues Add Offset by Normal command to move vertex along averge normal Make Duplicate command keep all joint influences Make Edge Divide command set group and texture coords Make Select Vertex tool automatically select and unselect triangles Select new face made by Make Face From Vertices Make texture coord window to reset map scheme on selection change Fix texture coord window displaying last texture if no triangle has a texture Merge Model / Import Animations Add support for frame animations to Import Animations Make Merge Model copy frame animation FPS Make Merge Model and Import Animations copy looping setting Fix Merge Model and Import Animations to set skeletal animation FPS on the correct animation Background Image Fix model using background image with Canvas mode Flat Fix model viewports not updating immediately after setting background image Fix drawing left/right brackground image in some cases Fix background image not blending in some cases Model Formats cal3d Add support for exporting cal3d .cfg files mm3d Fix writing background image filename GNU/Linux Update Flatpak runtime to org.kde.Platform 5.15-22.08 Changes for 1.3.13 (2021-12-20) General Add Clear Rotation/Translation Keyframes to Animation menu Add Base Point to Model Merge Add filename to Plugins window Allow removing vertexes from models with frame animations Allow adding/removing groups and points from models with frame animations Make splitting skeleton animation keep overall pose Make copy/paste skeleton animation frame copy interpolated keyframes Make paste animation frame select what was paste Make new animations default to non-looping Switch from QGLWidget to QOpenGLWidget (needed for Wayland on GNU/Linux, breaks Windows with OpenGL 1.1) Generate HTML manual using C instead of Perl with HTML::Template module Fix merged points in frame animations Fix crash if merging model when existing has a 0-frame animation Fix undo event for Merge Animations Fix model filter read/write to always use decimal point regardless of system locale Fix truncating frames when changing frame count in animation mode Fix Animation Sets -> Split to be start frame num Fix horizontal scroll events being treated as scroll down Fix texture coord window to check all selected triangles for a texture Fix select joint influenced points Fix Qt translations (qt_xx.qm) not being used Fix hundreds of compiler warnings Model Formats d3d (new) Add GameMaker Studio .d3d model import/export iqe Remove option to disable saving points in animations (IQM SDK would use identity, not bind pose) md2 Fix vertex normals orientation md3 Add export dialog with option to write animation.cfg for non-player models Don't allow saving as a player if there are groups that would not be saved in any player segment mm3d Deduplicate written external textures list Fix writing external texture count (it use to be material count which may be too high) Fix saving with long background filename causing out-of-bounds write ms3d Fix loading model with all zero vertex bone weights obj Fix crash if some MTL keywords are before newmtl Fix crash for out of bounds texcoord index GNU/Linux Add AppStream metadata and install icons into modern file path Add ability to build as a Flatpak Don't create 'shared' plugins symlink anymore (to allow multiple install paths) Rename mimetype from application/x-mm3d to model/x-mm3d Fix locating Qt translations on GNU/Linux Windows Support UTF-16 paths for install path and appdata on Windows Make building using MinGW on Debian use POSIX thread model so it uses the same GCC run-time as Qt Fix writing image filenames in models on a separate Windows drive than the images Fix listing files on Windows with ".." directory in path macOS Add system and Qt translations to macOS AppBundle Add `make appbundle` to Makefile.generic Add support for cross-compiling from GNU/Linux using osxcross Renamed macOS configure `make mm3d.app` target to `make appbundle` Build Note Moved autosetup.sh to autogen.sh to match defacto naming convention Changes for 1.3.12 (2019-10-07) Add shift key behavior to Texture Coord editor for move/rotate/scale Make Convert To Frame window convert multiple animations at once Remove artificial MD3 export limits Fix animation frame slider not responding to arrow keys Fix delete button in Edit Joints window Fix mouse interaction and wireframe line size on HiDPI displays Fix OpenGL possibly not working on Windows with Qt5 (black viewports) Fix some memory leak and std::list usage Fix configure when compiling on Fedora Changes for 1.3.11 (2019-01-02) Add support for exporting Studiomdl Data (SMD) models Add support for texture alpha blending in animation mode Add workaround to force toolbar to be visible even if it's hidden in dock.dat Improve decimal accuracy for rotation on properties panel Improve decimal accuracy in exported IQE models Improve MS3D model support Add support for loading/saving Milkshape 3D 1.8.5 BETA 1 MS3D models Add descriptions for vertex subversions to MS3D export prompt Only allow typing hexdecimal in MS3D Vertex Extra export prompt Don't allow exporting invalid MS3D models; too many vertexes, triangles, bone joints, groups, or materials Don't convert vertex bone weights of 0 to average for MS3D export Fix reading/writing MS3D animation bone joint keyframes Fix written MS3D bone joint weights not adding up to 100 Fix loading MS3D bone influences for more than 128 bones Fix loading MS3D when a group has no material Use Windows path separators in recent models list on Windows Fix writing line endings in OBJ/DXF/IQE on Windows (regression) Fix loading OBJ textures on Windows (regression) Fix Paint Texture window writing a blank black image (regression) Fix not loading texture the first time Texture Coordinates window is shown (regression) Fix some vertex bone joint weights being rounded down on properties panel and calculated auto weight Fix missing meta data undo event after Cal3D/MD3/MS3D export Fix wireframe Z-fighting with model meshes Fix new transparent texture set for material not being drawn Fix frame animated models default to blank animation type in Animation Sets window Fix gimbal lock detection when converting matrix to Euler angles (affected writing SMD models) Fix keyboard shortcuts in German and French translations Changes for 1.3.10 (2018-09-05) Rename application to Maverick Model 3D Add bone joint rotation to properties panel in non-animation mode Add press F1 for help to OBJ export options window Add support for autotools out-of-tree build Add support for exporting Inter-Quake Export (IQE) models Allow saving MD3 animations with 1 frame as looping Create window as maximized if it fills the available screen space Display result of Clean Up Groups in status bar Don't sort opaque RGBA textures in 3D Alpha Blend view mode Fix building for Windows Fix crash when opening Paint Texture Fix new/rename material window titles Fix rotating bone joint using properties panel in animation mode Fix saving MD3 with 1 animation and 1 frame writing unanimated vertexes Fix saving normals in MD3 player upper and head models Fix Texture Coordinates window being empty after closing it using title bar close button (Windows/macOS) Fix warning about unknown signal QTabWidget::selected when opening "Model|Background Image..." Fix zoom icons when compiled with newer Qt (worked on Qt 5.3 but not 5.10+) (use Qt 4+ resource system instead of embedding image in .ui files) Improve code integration of animation loop setting Load MD3 player skins with upper case extension (.SKIN) Make saving MD3 player head to use unanimated points and bounds Make subdivide command fail if no faces are selected Move animation Loop setting between FPS and Start/Stop buttons Only create vertex in Edge Divide if selected vertexes share a common face edge Print stdout/stderr to Windows command prompt RAW images are now loaded as little endian regardless of OS endianness Rename Materials|Clean Up Groups to Materials|Clean up Update plugin API usage for builtin features Use Win32 API to support non-ASCII filenames Changes for 1.3.9 (2018-02-11) Switch from Qt4 to Qt5 Make animation loop a per-animation setting that is saved in .mm3d files (mm3d format is now 1.7) Allow user to set fixed grid Fix incorrect texture rending for materials that share GL textures Fix insertFrameAnimFrame when frame is not at end Fix saving MD3 models with no frame animations Add a dialog to confirm saving a 3 part MD3 player model (before it always assumed yes) Made MD3 player support more general Read animation names / looping from animation.cfg (names fallback to Quake 3 ones if missing) Write animation names / looping from model animations instead of using a hard coded list Save all unknown points (e.g., not tag_torso, tag_head, or tag_weapon) as tags in all model parts Added support for "ALL_" (legs, torso, head) and "HEAD_" animation prefixes Don't require tag_weapon to export a MD3 player model Store options from animation.cfg in meta data "MD3_CFG_(keyword)" "(value)" instead of special handling for 'sex','footsteps','headoffset', 'fixedtorso','fixedlegs' which were stored as MD3_(keyword). MD3_sex, MD3_footsteps, etc are still supported for saving Add support for animation names to be at the beginning of each animation line in animation.cfg. Meta data "MD3_AnimKeyword" "1". Automatically detected when loading animation.cfg Allow disabling writing sync warnings in MD3 player animation.cfg using meta data "MD3_NoSyncWarning" "1". This is not automatically detected when loading animation.cfg Add support for Elite Force (Single Player) animation.cfg loop numbering style 0=loop all frames, -1=don't loop. Meta data "MD3_EliteLoop" "1". Automatically detected when loading animation.cfg Add support for loading / saving Quake III: Team Arena players Save point "tag_flag" in (only) the torso model Handle new Q3TA torso animations after legs in animation.cfg Add support for loading / saving Turtle Arena players Note: Exporting existing mm3d models to Quake 3 MD3 players requires setting the correct names and loop on animations. Misfit Model 3D releases published by Kevin Worcester. Changes for 1.3.8 (2009-03-09) Don't report power of two warning for background images Allow undo for texture coordinate vertex selection Rotate texture coordinates Horizontal/vertical flip texture coordinates Allow user to set color of texture coordinate lines and selection vertices. Include hidden triangles in BSP tree calculation, but don't render hidden triangles Change Hide Unselected so that it operates on the face level rather than face and vertex Add group clean-up window that removes identical/unused groups and materials Show dimensions of selected geometry in context panel Added grid unit distance to status bar In MD3, use anim name + frame num for frame name on export Use QString::toDouble instead of atof for localization reasons Fix incorrect group/texture mapping for copy/paste Fix viewport drawing mode so that it is applied to loaded models Fix texture rendering after deleted textures (off by one on greater index) Fix to draw proper normals for ungrouped geometry in skeletal animations Fix to prevent selection of hidden faces when using the "ignore back-facing" option Fix broken shortcuts on geometry sub-menus Fix coverage build/autoconf Fix MD2 export (vertex/texture coord indices) Fix relative path to parent dir for textures in OBJ format Fix cursor, reset coords, and zoom on texture coordinates window Fix help links on Windows Fix several compilation warnings Changes for 1.3.7 (2008-07-01) Added "Apply to selected" for Transform Model Export Selected (geometry, joints, points, and textures) Set exact position of rotate point for rotate tool More complete version support for Cal3D (700 through 1200) Read compressed animations in Cal3D Report error if saving MD2 or MD3 with faces that are not assigned to a group Fix to dissallow primitives additions using subdivide or addPoint Fix interpolation bug where rotation was not taking the shortest path to the new orientation Have a minimum animation redraw of 20 fps to show interpolation on slow animations Performance fixes for normal calculation (about 40% speed improvement) Performance fixes for MD3 loading (about 8x speed improvement) Added --enable-profile to ./configure Added --language to override the system locale Prevent select connected from selecting hidden faces Do not perform selection in frame animation mode if there are no animations Changed poly tool's "Fan" checkbox into a Strip/Fan combo box Implemented new equiv test and split CompareBits into CompareParts and PartProperties Changes for 1.3.6 (2008-01-14) Save/restore window size Include projection in frame all/selected command Open projection window from group and projection properties panels Allow multiple multiple bone joints for MS3D (subversions 1 and 2) Initial Cal3D support Better MD3_PATH handling Get MD3_PATH from shaders as well as model header Allow separate MD3_PATH for each shader Corrected capitalization for "Normals Face Out" command Split keyboard shortcut for flip front/back (backslash) view and flip ortho/persp (backtick) Added "Delete" button to animation model panel Redraw animation when loop is enabled/disabled (affects interpolation) Replaced lex/yacc code with custom preferences parser lex/yacc parser was having parse failures on some archs Allow MD2 texture coordinates to go outside of 0.0 to 1.0 range Report error if MD2 has more than one assigned material Remove trailing underscore on MD2 animation names Updated configuration for newer autoconf (2.61) and automake (1.9) Remember last skeletal/frame selection for new animation window Stop playing animation when a new animation is added. Error message when attempting to paste animation frame of wrong type Fixed bug where an influences with a weight of 0 has no influence, rather than placing the influenced object at the origin Fixed interpolation problem with translation keyframes on looped animations Fixed translation of animation name copy/split in animation sets window Fixed initial rotation of points for frame animations Other misc. translation fixes Added ../i18n to search path for translation files Treat unhandled backslashes in preferences as regular backslashes Changed error message on uknown file type to indicate it is probably a file extension problem Add ".mm3d" at save time if filename does not have an extension Use model vertex and point list sizes for writing frame animation data instead of using the size of the lists in the animation frames Added some sanity checking to MM3D loading code Removed several unused modules Added shell script for making source code tags Changes for 1.3.5 (2007-07-15) Re-organized menus Added "Rotate" tool in texture coordinate window Added "Face Out" command to make triangle normals point outward from enclosed hulls Added File|Export... to save without changing working filename or clearing unsaved flag Restrict Save and Save As to types with more complete support (everything else must use "Export...") Added Copy/Paste selected keyframe (for skeletal and frame) Added toolbar option on proj tool to create specific projection type Write texture coordinates for MM3D even if there are no materials MM3D no longer uses packed structs for mm3d, ms3d, tga, and pcx formats Improved material support for COB export Animation using Quaternion interpolation (prevent strange rotations when rotating on more than one axis) Fixed animation image export Fixed "Clear Animation Frame" for skeletal (broken when anim window was make dockable) Fix (hack) for Perspective label on all viewports Fixes for projection mapping at vertical extremes MD3 fixes: save case-insensitive animation names, cancel load if user selects "cancel" on player model Changes for 1.3.4 (2007-05-18) Support for internationalization Initial Slovak translation Show tool key accelerators in toolbar tool tip Rename bone joints and points from properties context panel Automatic bone joint assignment Extrude tool Option to create sphere from center Option to create torus from center Bring non-modal dialogs to front on open request when already open Fixes for MD2 (rotate orientation to match MM3D, prevent inversion of left/right, don't require animations to save/load), use MD2_PATH meta data for skin path instead of always assuming player model Fix for empty joint weight boxes Fix for starting in animation mode with animation toolbar hidden Changes for 1.3.3a (2007-03-02) Compilation fix for 64-bit architectures Changes for 1.3.3 (2007-02-28) Global transforms (translate, rotate, scale, arbitrary matrix) Disabled Lua by default Plane projection mapping Rotation on center of 3D viewport rather than origin Allow group material change from group properties panel Open group, material window from properties panel Can use orthographic projection from any angle (including tools) Can use Ctrl+Keyboard and Ctrl+ScrollButtons to rotate Rotate on Z axis (Ctrl+Mouse Wheel or Ctrl+Plus/Minus) Rotating fixed orthographic view switches to free orthographic automatically Hot key for saving and restoring viewport (Ctrl+Number to save, Number to restore) Tool to drag vertex on triangle Use tool keyboard shortcuts in texture coord window (Select Vertex, Move, Scale) Made texture coordinate window non-modal Texture coordinate window shows all selected faces (even if they do not belong to the same group) Scale tool in texture coordinate window has options for keeping aspect ratio and scaling from center Use Qt for drawing text in viewport instead of GLXFont (more portable) Fixed crash on alpha blending for non-texture materials Fixed OBJ material file creation on Win32 Fixed render menu selection indicators in Qt4/Win32 Changes for 1.3.2 (2007-01-23) Added "Influences Menu", all non-animation joint functions moved here Vertices and points can be assigned to multiple weighted bone joints Added "Paint Texture" window that saves a texture with the faces mapped onto it so that it can be painted over with a paint program Sphere and Cylinder mapping (Texture projection) Pan and zoom on texture coordinate edit window Keyboard shortcut customization (text file) Moved rendering options into a submenu of the View menu Added render option to turn off drawing of back-facing polygons Rotate tool rotation point defaults to center of selected objects Can create a new group by selecting "" in the group context panel Vertices can be created individually Made "Snap to Vertex" apply to bone joints, points, and projections Prevent viewport zoom/pan changes while tool is active Fixed MD3 crash on save without animations Fixed MD3 texture paths Fixed texture coordinates in MD2 for non-square skin images Fixed undo/redo selection on polygon tool Standardized enum declarations Standardized struct declarations Re-organized directory structure Corrected a typo in the View Window documentation Purge intermediate status bar messages if queue is too long (error messages not purged) Updated MM3D format documentation to include Points and Frame Anim Points Updated copyright dates Changes for 1.3.1 (2006-12-31) Added initial MD3 support (Initial filter written by Russell Valentine) Added Boolean operations (Union, Subtraction, Intersection) Added Simplify Mesh command Tint selected faces (uses lighting) User-defined viewport grid settings Allow setting multiple joint rotations at once (experimental) Allow changing/removing a texture on an existing material Added point rotation to properties window Added joint keyframe rotation to properties window Added points to the status bar primitive count Added .TXT model file format Added .RAW texture file format Use entire texture frame for 3D texture preview Do not exit on preferences parse failure Fixed looping animation frame time bug Fixed bolt point rotation and translation on model merge Fix for NaN in Matrix::getRotation() Fixed scoping error for gcc 4.1.1 Added virtual destructor for Model::Observer Changes for 1.3.0 from 1.2.0-final (2006-09-16) Snap to Grid Snap to Vertex Converted animation window to smaller, dockable window Added context-sensitive panel for editing position and group properties (more functionality to come) See View -> Show Properties Edge Turn command Edge Divide command Cap holes command Fixes for ./configure with 64-bit libraries Fixes for 64-bit safe (draw context, and pointer debug output) Initial bolt point support Initial .cob and .dxf support (read/write geometry and materials) Fixed crash on calculateFrameNormals if animation is out of range Append underscore to MD2 animation names that end in numbers to prevent truncation of animation name Change MD2 loading code to remove trailing digits instead of truncating at the first digit Polygon tool creates triangle to face tool viewport Fix for Face select in Frame Animation mode (patch from Russell Valentine) Fix for MD2 GL commands section (terminate with 0) Fixed scaling for co-axial points (division by zero causing undefined coordinates) Removed qpixmap uic hack in the Makefile mm3d-1.3.15/INSTALL000066400000000000000000000452031466047437300135140ustar00rootroot00000000000000Maverick Model 3D Installation ============================== If you are running on Windows see INSTALL.WIN32. If you are running on a Unix-like system, installation instructions follow. Maverick Model 3D requires the following packages for proper operation. They are most likely already installed if you have a modern working Linux system. Qt (5.x) https://download.qt.io/archive/qt/ OpenGL with GLU (probably Mesa if you are using Linux) http://www.mesa3d.org/ Even if you have these packages installed you may need to install development packages as well. These include header files which are needed to compile Maverick Model 3D. Often such packages are marked as -dev or -devel by your distribution. For example, sometimes Qt is broken up into a qt rpm and qt-devel rpm. In this case, you must have both installed. This program uses autoconf and automake for building from source. What this means is that if you are lucky you can install this program by running these commands at a shell prompt (terminal window): ./autosetup.sh ./configure make sudo make install On macOS, use the commands listed in the README to create an App Bundle instead. The default PREFIX (install location) is /usr/local. Files are installed in the following directories. PREFIX/bin - mm3d executable PREFIX/share/mm3d/plugins - plugins PREFIX/share/doc/mm3d - program documentation PREFIX/share/applications - program menu entry PREFIX/share/mimelnk/application - .mm3d file integration PREFIX/share/pixmaps - program menu icon PREFIX/share/man/man1 - program manual You can give ./configure arguments to change how the program is compiled. For example you can change the default install location (PREFIX) by specifying --prefix=PATH, where PATH is the new install location. If Qt is not detected and you have it installed, try using --with-Qt-dir to specify the directory where Qt is installed. In a default Qt installation this is what your QTDIR environment variable is set to. Include files are in $QTDIR/include and library files are in $QTDIR/lib. Sometimes development files are in separate paths completely, such as libs in /usr/lib/qt5 and headers in /usr/include/qt5. In this case use --with-Qt-include-dir=/usr/include/qt5 and --with-Qt-lib-dir=/usr/lib/qt5. If you want to link against a specific library of Qt you can use --with-Qt-lib=LIBRARY to specify the library. For example, if you want to force the multi-threaded Qt library, you would use --with-Qt-lib=qt-mt Use ./configure --help for a complete list of options. Below are generic instructions for installing programs using autoconf and automake. They are provided for reference if you need more help using ./configure. Installation Instructions ************************* These are generic installation instructions. Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command './configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the 'README' file for instructions specific to this package. Some packages provide this 'INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The 'configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a 'Makefile' in each directory of the package. It may also create one or more '.h' files containing system-dependent definitions. Finally, it creates a shell script 'config.status' that you can run in the future to recreate the current configuration, and a file 'config.log' containing compiler output (useful mainly for debugging 'configure'). It can also use an optional file (typically called 'config.cache' and enabled with '--cache-file=config.cache' or simply '-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how 'configure' could check whether to do them, and mail diffs or instructions to the address given in the 'README' so they can be considered for the next release. If you are using the cache, and at some point 'config.cache' contains results you don't want to keep, you may remove or edit it. The file 'configure.ac' (or 'configure.in') is used to create 'configure' by a program called 'autoconf'. You need 'configure.ac' if you want to change it or regenerate 'configure' using a newer version of 'autoconf'. The simplest way to compile this package is: 1. 'cd' to the directory containing the package's source code and type './configure' to configure the package for your system. Running 'configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type 'make' to compile the package. 3. Optionally, type 'make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type 'make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the 'make install' phase executed with root privileges. 5. Optionally, type 'make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior 'make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing 'make clean'. To also remove the files that 'configure' created (so you can compile the package for a different kind of computer), type 'make distclean'. There is also a 'make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type 'make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide 'make distcheck', which can by used by developers to test that all other targets like 'make install' and 'make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the 'configure' script does not know about. Run './configure --help' for details on some of the pertinent environment variables. You can give 'configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU 'make'. 'cd' to the directory where you want the object files and executables to go and run the 'configure' script. 'configure' automatically checks for the source code in the directory that 'configure' is in and in '..'. This is known as a "VPATH" build. With a non-GNU 'make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use 'make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple '-arch' options to the compiler but only a single '-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the 'lipo' tool if you have problems. Installation Names ================== By default, 'make install' installs the package's commands under '/usr/local/bin', include files under '/usr/local/include', etc. You can specify an installation prefix other than '/usr/local' by giving 'configure' the option '--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option '--exec-prefix=PREFIX' to 'configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like '--bindir=DIR' to specify different values for particular kinds of files. Run 'configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of '${prefix}', so that specifying just '--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to 'configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the 'make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, 'make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of '${prefix}'. Any directories that were specified during 'configure', but not in terms of '${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the 'DESTDIR' variable. For example, 'make install DESTDIR=/alternate/directory' will prepend '/alternate/directory' before all installation names. The approach of 'DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of '${prefix}' at 'configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving 'configure' the option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. Some packages pay attention to '--enable-FEATURE' options to 'configure', where FEATURE indicates an optional part of the package. They may also pay attention to '--with-PACKAGE' options, where PACKAGE is something like 'gnu-as' or 'x' (for the X Window System). The 'README' should mention any '--enable-' and '--with-' options that the package recognizes. For packages that use the X Window System, 'configure' can usually find the X include and library files automatically, but if it doesn't, you can use the 'configure' options '--x-includes=DIR' and '--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of 'make' will be. For these packages, running './configure --enable-silent-rules' sets the default to minimal output, which can be overridden with 'make V=1'; while running './configure --disable-silent-rules' sets the default to verbose, which can be overridden with 'make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX 'make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as 'configure' are involved. Use GNU 'make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its '' header file. The option '-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put '/usr/ucb' early in your 'PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in '/usr/bin'. So, if you need '/usr/ucb' in your 'PATH', put it _after_ '/usr/bin'. On Haiku, software installed for all users goes in '/boot/common', not '/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features 'configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, 'configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the '--build=TYPE' option. TYPE can either be a short name for the system type, such as 'sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file 'config.sub' for the possible values of each field. If 'config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option '--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with '--host=TYPE'. Sharing Defaults ================ If you want to set default values for 'configure' scripts to share, you can create a site shell script called 'config.site' that gives default values for variables like 'CC', 'cache_file', and 'prefix'. 'configure' looks for 'PREFIX/share/config.site' if it exists, then 'PREFIX/etc/config.site' if it exists. Or, you can set the 'CONFIG_SITE' environment variable to the location of the site script. A warning: not all 'configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to 'configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the 'configure' command line, using 'VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified 'gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 'configure' Invocation ====================== 'configure' recognizes the following options to control how it operates. '--help' '-h' Print a summary of all of the options to 'configure', and exit. '--help=short' '--help=recursive' Print a summary of the options unique to this package's 'configure', and exit. The 'short' variant lists options used only in the top level, while the 'recursive' variant lists options also present in any nested packages. '--version' '-V' Print the version of Autoconf used to generate the 'configure' script, and exit. '--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally 'config.cache'. FILE defaults to '/dev/null' to disable caching. '--config-cache' '-C' Alias for '--cache-file=config.cache'. '--quiet' '--silent' '-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to '/dev/null' (any error messages will still be shown). '--srcdir=DIR' Look for the package's source code in directory DIR. Usually 'configure' can determine that directory automatically. '--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. '--no-create' '-n' Run the configure checks, but stop before creating any output files. 'configure' also accepts some other, not widely useful, options. Run 'configure --help' for more details. mm3d-1.3.15/INSTALL.WIN32000066400000000000000000000123411466047437300143120ustar00rootroot00000000000000Maverick Model 3D Installation For Win32 ======================================== Maverick Model 3D runs on Windows 7 and later. It may run on Windows XP and Vista if you compile Qt 5 with Windows XP support. It will not work on older Windows versions. Maverick Model 3D for Windows requires the MinGW environment and the Open Source version of Qt 5 for Windows. To run binaries of mm3d you only need the Qt and MinGW DLLs, which are provided with the binary distribution. To build from source you must have a development environment set up with MinGW and Qt 5. Binary Installation =================== Get the latest installer from the web site. It should have a filename like 'mm3d-X_X_X-win32-installer.exe'. Run the installer and select an install location. Optionally you may choose to enable file assciations with Maverick Model 3D. By default only .mm3d is associated. Building in MSYS2 ================= This is an easy way to build for your own personal use however making releases is not supported. MSYS2 has Qt depend on various libraries and they aren't copied to the install directory by windeployqt and would not be added to the MM3D NSIS installer. 1. Install MSYS2 and follow the guide for updating packages at . You don't need to install the recommended base-devel or mingw toolchain packages though. 2. Open "MSYS2 MSYS" from the start menu and run the following command to install the necessary packages: pacman -S --needed git make mingw-w64-x86_64-{gcc,gdb} mingw-w64-x86_64-qt5-{base,imageformats,svg,tools,translations} If you need to debug the application, you may want these as well: pacman -S --needed mingw-w64-x86_64-qt5-{base,imageformats,svg,tools}-debug 3. Open "MSYS2 MinGW 64-Bit" and run the following commands. They change to the Desktop folder, download Maverick from GitHub, build using the generic config.h and Makefile, and then "install" into Desktop/mm3d/build/mingw64-x86_64/install. (Feel free to use a folder besides Desktop.) cd /c/Users/$USERNAME/Desktop git clone https://github.com/zturtleman/mm3d.git cd mm3d cp config.h.generic config.h cp Makefile.generic Makefile echo "QTDIR=/mingw64" > Makefile.local make make install 4. Still in "MSYS2 MinGW 64-Bit" run the following to launch Maverick: PATH=/mingw64/bin:$PATH build/mingw64-x86_64/install/mm3d.x86_64.exe If you edit the Makefile or it's failing to build after updating packages do a fresh build using the following: make clean make Attempting to run build/mingw64-x86_64/install/mm3d.x86_64.exe without setting PATH enviornment variable (or copying to another PC without MSYS2) will report errors about missing DLLs. Copy them using Windows Explorer from C:\msys64\mingw64\bin\ or using "MSYS2 MinGW 64-Bit" from /mingw64/bin/ : cd build/mingw64-x86_64/install cp /mingw64/bin/libbrotilcommon.dll . cp /mingw64/bin/libbrotildec.dll . cp /mingw64/bin/libbz2-1.dll . cp /mingw64/bin/libdouble-conversion.dll . cp /mingw64/bin/libfreetype-6.dll . cp /mingw64/bin/libglib-2-0-0.dll . cp /mingw64/bin/libgraphite2.dll . cp /mingw64/bin/libharfbuzz-0.dll . cp /mingw64/bin/libiconv-2.dll . cp /mingw64/bin/libicudt*.dll . cp /mingw64/bin/libicuin*.dll . cp /mingw64/bin/libicuuc*.dll . cp /mingw64/bin/libintl-8 . cp /mingw64/bin/libmd4c.dll . cp /mingw64/bin/libpcre-1.dll . cp /mingw64/bin/libpng16-16.dll . cp /mingw64/bin/libzstd.dll . cp /mingw64/bin/pcre2-16-0.dll . cp /mingw64/bin/zlib1.dll . cd ../../.. Below are directions for building using the official Qt installer. Though it has not been tested with recent Qt 5 versions. Prerequisites ============= You can get Qt 5.x here: https://www.qt.io/ During the Qt 5 installer make you sure you check the box for installing Qt 5.11.1 and MinGW 5.3 (or the latest versions). You must set your QTDIR environment variable to the root Qt directory (for example, "C:\Qt\5.11.1\mingw53_32"). The Mingw bin directory must be added to your PATH environment variable (for example, append "C:\Qt\Tools\mingw530_32\bin;"). Build Instructions ================== Once you have installed Qt and MinGW, do the following steps: 1. Make sure QTDIR is set and MinGW's bin directory is in your PATH 2. Open a cmd prompt and go to the mm3d-X.X.X directory 3. Copy config.h.generic to config.h 4. Run 'mingw32-make -f Makefile.generic MINGW32_MAKE=1' Install Instructions ==================== 1. Run 'mingw32-make -f Makefile.generic MINGW32_MAKE=1 deploy' from the mm3d-X.X.X directory. 2. Copy the build\mingw32-x86\install\ directory to where ever you want. Settings are saved in %APPDATA$\Maverick Model 3D\ `make deploy` does not create any program menu or desktop shortcuts. If you want shortcuts you must create them manually, or use the binary installer. Optionally, you can install NSIS and then build the installer. This will create shortcuts and some registry keys. You can get NSIS here: http://nsis.sourceforge.net/ 3. Run 'mingw32-make -f Makefile.generic MINGW32_MAKE=1 installer' from the mm3d-X.X.X directory. mm3d-1.3.15/Makefile.am000066400000000000000000000153741466047437300145250ustar00rootroot00000000000000EXTRA_DIST = Makefile.generic \ config.h.generic \ cleanup.sh \ mm3d-win32-installer.nsi \ TRANSLATORS \ INSTALL.WIN32 \ mm3d.ico \ mm3d.icns # util is before doc because doc uses tool built in util. SUBDIRS = src plugins translations util doc desktop man DESTDIR = src MAC_APP = Maverick Model 3D.app define INFOPLIST CFBundleDevelopmentRegion en UTExportedTypeDeclarations UTTypeConformsTo public.data UTTypeIdentifier moe.clover.mm3dmodel UTTypeDescription MM3D model UTTypeIconFile mm3d UTTypeReferenceURL https://clover.moe/mm3d_manual/olh_mm3dformat.html UTTypeTagSpecification public.filename-extension mm3d CFBundleDocumentTypes CFBundleTypeName MM3D model LSItemContentTypes moe.clover.mm3dmodel CFBundleTypeRole Editor LSHandlerRank Owner CFBundleTypeName Quake 2 model CFBundleTypeExtensions md2 CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleTypeName Quake 3 model CFBundleTypeExtensions md3 CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleTypeName Milkshape 3D model CFBundleTypeExtensions ms3d CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleTypeName Wavefront OBJ model CFBundleTypeExtensions obj CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleExecutable ${PACKAGE} CFBundleIconFile ${PACKAGE} CFBundleIdentifier moe.clover.${PACKAGE} CFBundleInfoDictionaryVersion 6.0 CFBundleName Maverick Model 3D CFBundlePackageType APPL CFBundleShortVersionString ${VERSION} CFBundleSignature ???? CFBundleVersion ${VERSION} CGDisableCoalescedUpdates LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSHumanReadableCopyright Copyright © 2004-2008 Kevin Worcester, Copyright © 2009-2024 Zack Middleton. NSPrincipalClass NSApplication endef export INFOPLIST define LOCVERSIONPLIST LprojCompatibleVersion 123 LprojLocale XXLANGXX LprojRevisionLevel 1 LprojVersion 123 endef export LOCVERSIONPLIST appbundle: ${INSTALL} -d "$(MAC_APP)/Contents/MacOS/" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/" ${INSTALL} -d "$(MAC_APP)/Contents/PlugIns/mm3d/1.3" ${INSTALL} -d "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/screencaps/" ${INSTALL} -d "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/tools/" ${INSTALL} -d "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/" ${INSTALL} -m 0755 "$(DESTDIR)/$(PACKAGE)" "$(MAC_APP)/Contents/MacOS/$(PACKAGE)" ${INSTALL} -m 0644 $(top_srcdir)/mm3d.icns "$(MAC_APP)/Contents/Resources/${PACKAGE}.icns" ${INSTALL} -m 0644 doc/html/*.html "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/" ${INSTALL} -m 0644 $(top_srcdir)/doc/html/olh_images/screencaps/*.png "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/screencaps/" ${INSTALL} -m 0644 $(top_srcdir)/doc/html/olh_images/tools/*.png "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/tools/" ${INSTALL} -m 0644 $(top_srcdir)/doc/html/olh_images/tools/*.jpg "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/tools/" ${INSTALL} -m 0644 translations/*.qm "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/" echo "APPL????" > "$(MAC_APP)/Contents/PkgInfo" @echo "$$INFOPLIST" > "$(MAC_APP)/Contents/Info.plist" @echo "Wrote \"$(MAC_APP)/Contents/Info.plist\"" $(QT_MACDEPLOYQT) "$(MAC_APP)" @# Add translations of Qt itself $(QT_LCONVERT) -o "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/qt_de.qm" "$(QT_TRANSLATIONS_DIR)/qtbase_de.qm" $(QT_LCONVERT) -o "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/qt_fr.qm" "$(QT_TRANSLATIONS_DIR)/qtbase_fr.qm" $(QT_LCONVERT) -o "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/qt_sk.qm" "$(QT_TRANSLATIONS_DIR)/qtbase_sk.qm" @# Tell macOS to translate builtin dialogs (i.e., file chooser) ${INSTALL} -d "$(MAC_APP)/Contents/Resources/en.lproj" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/de.lproj" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/fr.lproj" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/sk.lproj" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/en/g" > "$(MAC_APP)/Contents/Resources/en.lproj/locversion.plist" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/de/g" > "$(MAC_APP)/Contents/Resources/de.lproj/locversion.plist" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/fr/g" > "$(MAC_APP)/Contents/Resources/fr.lproj/locversion.plist" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/sk/g" > "$(MAC_APP)/Contents/Resources/sk.lproj/locversion.plist" mm3d-1.3.15/Makefile.generic000066400000000000000000001160241466047437300155360ustar00rootroot00000000000000# # Maverick Model 3D Makefile # # GNU Make required # # mingw.org's mingw32-make.exe on Windows doesn't have uname ifdef MINGW32_MAKE COMPILE_PLATFORM=mingw32 COMPILE_ARCH=x86 else COMPILE_PLATFORM=$(shell uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'|sed -e 's/\//_/g') COMPILE_ARCH=$(shell uname -m | sed -e s/i.86/x86/) ifeq ($(COMPILE_PLATFORM),sunos) # Solaris uname and GNU uname differ COMPILE_ARCH=$(shell uname -p | sed -e s/i.86/x86/) endif endif ############################################################################# # # If you require a different configuration from the defaults below, create a # new file named "Makefile.local" in the same directory as this file and define # your parameters there. This allows you to change configuration without # causing problems with keeping up to date with the repository. # ############################################################################# -include Makefile.local ifeq ($(COMPILE_PLATFORM),cygwin) PLATFORM=mingw32 endif ifndef PLATFORM PLATFORM=$(COMPILE_PLATFORM) endif export PLATFORM ifeq ($(PLATFORM),mingw32) MINGW=1 endif ifeq ($(PLATFORM),mingw64) MINGW=1 endif ifeq ($(COMPILE_ARCH),powerpc) COMPILE_ARCH=ppc endif ifeq ($(COMPILE_ARCH),powerpc64) COMPILE_ARCH=ppc64 endif ifndef ARCH ARCH=$(COMPILE_ARCH) endif export ARCH ifneq ($(PLATFORM),$(COMPILE_PLATFORM)) CROSS_COMPILING=1 else CROSS_COMPILING=0 ifneq ($(ARCH),$(COMPILE_ARCH)) CROSS_COMPILING=1 endif endif export CROSS_COMPILING ifndef V V= endif ifndef BUILD BUILD=build endif bin_path=$(shell which $(1) 2> /dev/null) BUILDSUB=${PLATFORM}-${ARCH} B=${BUILD}/${BUILDSUB} BUILDSUB_TOOLS=${COMPILE_PLATFORM}-${COMPILE_ARCH} B_TOOLS=${BUILD}/${BUILDSUB_TOOLS} INSTALL=install ifdef MINGW32_MAKE MKDIR=mkdir else MKDIR=mkdir -p endif ############################################################################# # SETUP AND BUILD -- MINGW ############################################################################# ifdef MINGW ifndef QTDIR ifneq ($(CROSS_COMPILING),1) QTDIR=C:/Qt/5.11.1/mingw53_32 ifdef MINGW32_MAKE CC=C:\Qt\Tools\mingw530_32\bin\i686-w64-mingw32-gcc-5.3.0.exe CXX=C:\Qt\Tools\mingw530_32\bin\i686-w64-mingw32-g++.exe endif endif endif ifdef QTDIR ifndef QT_BIN_DIR QT_BIN_DIR=${QTDIR}/bin endif ifndef QT_INCLUDE_DIR QT_INCLUDE_DIR=${QTDIR}/include endif ifndef QT_LIB_DIR QT_LIB_DIR=${QTDIR}/lib endif ifndef QT_TRANSLATIONS_DIR QT_TRANSLATIONS_DIR=${QTDIR}/translations endif endif ifndef QT_BIN_DIR $(error Need to specify QT_BIN_DIR with native Qt5 executables for uic, moc, rcc, and lrelease) endif ifndef QT_INCLUDE_DIR $(error Need to specify QT_INCLUDE_DIR with MinGW Qt5 headers) endif ifndef QT_LIB_DIR $(error Need to specify QT_LIB_DIR with MinGW Qt5 libs) endif # not required, it's only used on macOS right now #ifndef QT_TRANSLATIONS_DIR # $(error Need to specify QT_TRANSLATIONS_DIR with MinGW Qt5 translations) #endif ifeq ($(CROSS_COMPILING),1) # If CC is already set to something generic, we probably want to use # something more specific ifneq ($(findstring $(strip $(CC)),cc gcc),) CC= endif ifneq ($(findstring $(strip $(CXX)),g++),) CXX= endif # We need to figure out the correct gcc and windres ifeq ($(ARCH),x86_64) MINGW_PREFIXES=x86_64-w64-mingw32 amd64-mingw32msvc endif ifeq ($(ARCH),x86) MINGW_PREFIXES=i686-w64-mingw32 i586-mingw32msvc i686-pc-mingw32 endif # Prefer POSIX thread model (as opposed to win32) to use the same # libgcc and libstdc++ run-time as Qt. Debian has separate binaries # with -posix and -win32 suffix. "$(MINGW_PREFIX)-gcc" could be either. ifndef CC CC=$(strip $(foreach MINGW_PREFIX, $(MINGW_PREFIXES), \ $(call bin_path, $(MINGW_PREFIX)-gcc-posix))) endif ifndef CC CC=$(strip $(foreach MINGW_PREFIX, $(MINGW_PREFIXES), \ $(call bin_path, $(MINGW_PREFIX)-gcc))) endif ifndef CXX CXX=$(strip $(foreach MINGW_PREFIX, $(MINGW_PREFIXES), \ $(call bin_path, $(MINGW_PREFIX)-g++-posix))) endif ifndef CXX CXX=$(strip $(foreach MINGW_PREFIX, $(MINGW_PREFIXES), \ $(call bin_path, $(MINGW_PREFIX)-g++))) endif ifndef WINDRES WINDRES=$(strip $(foreach MINGW_PREFIX, $(MINGW_PREFIXES), \ $(call bin_path, $(MINGW_PREFIX)-windres))) endif TOOLS_BINEXT= else # Some MinGW installations define CC to cc, but don't actually provide cc, # so check that CC points to a real binary and use gcc if it doesn't ifeq ($(call bin_path, $(CC)),) CC=gcc endif ifndef CXX CXX=g++ endif ifndef WINDRES WINDRES=windres endif TOOLS_BINEXT=.exe endif ifeq ($(CC),) $(error Cannot find a suitable cross compiler for $(PLATFORM)) endif ifdef MINGW32_MAKE # mingw32 windres needs absolute path to compiler for using as a preprocessor ifeq ("$(wildcard $(CC))","") # $(error CC must be absolute path to gcc) endif endif ifndef QT_LIBS QT_LIBS=-lQt5Core -lQt5Gui -lQt5Widgets endif LINK=-L${QT_LIB_DIR} ${QT_LIBS} -mwindows -lshlwapi -lole32 -lopengl32 -lglu32 # NOTE: If building using gcc/g++ with win32 thread model or a different # toolchain than Qt was built with you may want to use static libgcc # and libstdc++. #LINK+= -static-libgcc -static-libstdc++ BASE_FLAGS+=-DWIN32 BINEXT=.${ARCH}.exe else # ifeq mingw ############################################################################# # SETUP AND BUILD -- MAC OS X ############################################################################# ifeq ($(PLATFORM),darwin) ifndef PREFIX PREFIX=/usr/local endif ifndef QTDIR QTDIR=/usr/local/Cellar/qt/5.11.1 endif ifndef QT_BIN_DIR QT_BIN_DIR=${QTDIR}/bin endif ifndef QT_INCLUDE_DIR QT_INCLUDE_DIR=${QTDIR}/include endif ifndef QT_LIB_DIR QT_LIB_DIR=${QTDIR}/lib endif ifndef QT_FRAMEWORKS_DIR QT_FRAMEWORKS_DIR=${QTDIR}/lib endif ifndef QT_TRANSLATIONS_DIR QT_TRANSLATIONS_DIR=${QTDIR}/translations endif ifndef QT_LIBS QT_LIBS= endif ifndef QT_FRAMEWORKS QT_FRAMEWORKS=-framework QtCore -framework QtGui -framework QtWidgets endif FRAMEWORKS=${QT_FRAMEWORKS} -framework OpenGL LINK=-F${QT_FRAMEWORKS_DIR} ${FRAMEWORKS} -L${QT_LIB_DIR} ${QT_LIBS} # Required for C++11 on macOS BASE_FLAGS+=-stdlib=libc++ ifeq ($(ARCH),x86) BASE_FLAGS += -m32 endif BASE_FLAGS += -DPREFIX="\"$(PREFIX)\"" -DIS_OSX # Set this to match Qt's minimum macOS deployment target. ifdef MACOSX_VERSION_MIN BASE_FLAGS += -mmacosx-version-min=${MACOSX_VERSION_MIN} endif MACOSX_DEPLOYMENT_TARGET=$(MACOSX_VERSION_MIN) BASE_FLAGS += -F${QT_FRAMEWORKS_DIR} BINEXT=.${ARCH} TOOLS_BINEXT= else # ifeq darwin ############################################################################# # SETUP AND BUILD -- LINUX ############################################################################# ifndef PREFIX PREFIX=/usr/local endif ifdef QTDIR ifndef QT_BIN_DIR QT_BIN_DIR=${QTDIR}/bin endif ifndef QT_INCLUDE_DIR QT_INCLUDE_DIR=${QTDIR}/include endif ifndef QT_LIB_DIR QT_LIB_DIR=${QTDIR}/lib endif ifndef QT_TRANSLATIONS_DIR QT_TRANSLATIONS_DIR=${QTDIR}/translations endif else ifndef QT_BIN_DIR ifeq ($(ARCH),x86) QT_BIN_DIR=/usr/lib/i386-linux-gnu/qt5/bin else QT_BIN_DIR=/usr/lib/$(ARCH)-linux-gnu/qt5/bin endif endif ifndef QT_INCLUDE_DIR ifeq ($(ARCH),x86) QT_INCLUDE_DIR=/usr/include/i386-linux-gnu/qt5 else QT_INCLUDE_DIR=/usr/include/$(ARCH)-linux-gnu/qt5 endif endif ifndef QT_LIB_DIR ifeq ($(ARCH),x86) QT_LIB_DIR=/usr/lib/i386-linux-gnu else QT_LIB_DIR=/usr/lib/$(ARCH)-linux-gnu endif endif ifndef QT_TRANSLATIONS_DIR QT_TRANSLATIONS_DIR=/usr/share/qt5/translations/ endif endif ifndef QT_LIBS QT_LIBS=-lQt5Core -lQt5Gui -lQt5Widgets endif LINK=-L${QT_LIB_DIR} ${QT_LIBS} -lGLU -lGL -ldl ifeq ($(ARCH),x86) # linux32 make ... BASE_FLAGS += -m32 else ifeq ($(ARCH),ppc64) BASE_FLAGS += -m64 endif endif BASE_FLAGS += -DPREFIX="\"$(PREFIX)\"" BINEXT=.${ARCH} TOOLS_BINEXT= endif # darwin endif # mingw ifndef QT_BIN_DIR $(error QT_BIN_DIR is not set) endif ifndef QT_INCLUDE_DIR $(error QT_INCLUDE_DIR is not set) endif ifndef UIC UIC=${QT_BIN_DIR}/uic${TOOLS_BINEXT} endif ifndef MOC MOC=${QT_BIN_DIR}/moc${TOOLS_BINEXT} endif ifndef RCC RCC=${QT_BIN_DIR}/rcc${TOOLS_BINEXT} endif ifndef LRELEASE LRELEASE=${QT_BIN_DIR}/lrelease${TOOLS_BINEXT} endif ifndef LCONVERT LCONVERT=${QT_BIN_DIR}/lconvert${TOOLS_BINEXT} endif ifndef WINDEPLOYQT WINDEPLOYQT=${QT_BIN_DIR}/windeployqt${TOOLS_BINEXT} endif ifndef MACDEPLOYQT MACDEPLOYQT=${QT_BIN_DIR}/macdeployqt${TOOLS_BINEXT} endif ifndef MINGW # not for mingw because warning: -fPIC ignored for target (all code is position independent) BASE_FLAGS+=-fPIC endif BASE_FLAGS+=-fno-math-errno -I${QT_INCLUDE_DIR} -DMM3D_EDIT -I. -I./src -I./src/libmm3d -I./src/mm3dcore -I./src/depui -I./src/qtui -I./src/implui -I./src/commands -I./src/tools -I${B}/depui -I${B}/qtui -I${B}/implui -I${B}/commands # Generate dependencies (create .d files by the .o files) BASE_FLAGS+=-MMD -MP # Use debug or release cflags #BUILD_FLAGS+=-Wall -Wextra -Wno-unused-parameter -Wno-format-zero-length -g -ggdb #STRIP= BUILD_FLAGS+=-Wall -Wextra -Wno-unused-parameter -Wno-format-zero-length -O2 STRIP=strip CCFLAGS=-std=c99 ${BUILD_FLAGS} ${BASE_FLAGS} CXXFLAGS=-std=c++11 ${BUILD_FLAGS} ${BASE_FLAGS} # Use a native compiler for compiling build tools. ifeq ($(CROSS_COMPILING),1) TOOLS_CC=cc else TOOLS_CC=${CC} endif TOOLS_CCFLAGS=-std=c99 ${BUILD_FLAGS} DEFS= BIN=${B} LIB_OBJ= \ ${B}/libmm3d/bsptree.o \ ${B}/libmm3d/cal3dfilter.o \ ${B}/libmm3d/cmdlinemgr.o \ ${B}/libmm3d/cobfilter.o \ ${B}/libmm3d/d3dfilter.o \ ${B}/libmm3d/datadest.o \ ${B}/libmm3d/datasource.o \ ${B}/libmm3d/filedatadest.o \ ${B}/libmm3d/filedatasource.o \ ${B}/libmm3d/memdatadest.o \ ${B}/libmm3d/memdatasource.o \ ${B}/libmm3d/dxffilter.o \ ${B}/libmm3d/filefactory.o \ ${B}/libmm3d/filtermgr.o \ ${B}/libmm3d/glmath.o \ ${B}/libmm3d/iqefilter.o \ ${B}/libmm3d/log.o \ ${B}/libmm3d/lwofilter.o \ ${B}/libmm3d/md2filter.o \ ${B}/libmm3d/md3filter.o \ ${B}/libmm3d/mesh.o \ ${B}/libmm3d/misc.o \ ${B}/libmm3d/mlocale.o \ ${B}/libmm3d/mm3dfilter.o \ ${B}/libmm3d/mm3dport.o \ ${B}/libmm3d/mm3dreg.o \ ${B}/libmm3d/model.o \ ${B}/libmm3d/model_anim.o \ ${B}/libmm3d/model_bool.o \ ${B}/libmm3d/model_copy.o \ ${B}/libmm3d/model_draw.o \ ${B}/libmm3d/model_group.o \ ${B}/libmm3d/model_influence.o \ ${B}/libmm3d/model_inner.o \ ${B}/libmm3d/model_insert.o \ ${B}/libmm3d/model_meta.o \ ${B}/libmm3d/model_ops.o \ ${B}/libmm3d/model_print.o \ ${B}/libmm3d/model_proj.o \ ${B}/libmm3d/model_select.o \ ${B}/libmm3d/model_texture.o \ ${B}/libmm3d/modelfilter.o \ ${B}/libmm3d/modelstatus.o \ ${B}/libmm3d/modelundo.o \ ${B}/libmm3d/modelutil.o \ ${B}/libmm3d/ms3dfilter.o \ ${B}/libmm3d/msg.o \ ${B}/libmm3d/objfilter.o \ ${B}/libmm3d/pcxtex.o \ ${B}/libmm3d/rawtex.o \ ${B}/libmm3d/smdfilter.o \ ${B}/libmm3d/texmgr.o \ ${B}/libmm3d/texscale.o \ ${B}/libmm3d/texture.o \ ${B}/libmm3d/tgatex.o \ ${B}/libmm3d/triprim.o \ ${B}/libmm3d/txtfilter.o \ ${B}/libmm3d/undo.o \ ${B}/libmm3d/undomgr.o \ ${B}/libmm3d/weld.o \ MM3D_UI= \ ${B}/qtui/alignwin.base.h \ ${B}/qtui/animconvertwin.base.h \ ${B}/qtui/animeditwin.base.h \ ${B}/qtui/animexportwin.base.h \ ${B}/qtui/animsetwin.base.h \ ${B}/qtui/animwidget.base.h \ ${B}/qtui/autoassignjointwin.base.h \ ${B}/qtui/backgroundselect.base.h \ ${B}/qtui/backgroundwin.base.h \ ${B}/qtui/boolwin.base.h \ ${B}/qtui/contextgroup.base.h \ ${B}/qtui/contextinfluences.base.h \ ${B}/qtui/contextname.base.h \ ${B}/qtui/contextposition.base.h \ ${B}/qtui/contextprojection.base.h \ ${B}/qtui/contextrotation.base.h \ ${B}/qtui/extrudewin.base.h \ ${B}/qtui/groupclean.base.h \ ${B}/qtui/groupwin.base.h \ ${B}/qtui/helpwin.base.h \ ${B}/qtui/jointwin.base.h \ ${B}/qtui/painttexturewin.base.h \ ${B}/qtui/pointwin.base.h \ ${B}/qtui/projectionwin.base.h \ ${B}/qtui/keyvaluewin.base.h \ ${B}/qtui/mapdirection.base.h \ ${B}/qtui/mergewin.base.h \ ${B}/qtui/metawin.base.h \ ${B}/qtui/modelview.base.h \ ${B}/qtui/newanim.base.h \ ${B}/qtui/cal3dprompt.base.h \ ${B}/qtui/iqeprompt.base.h \ ${B}/qtui/md3prompt.base.h \ ${B}/qtui/ms3dprompt.base.h \ ${B}/qtui/objprompt.base.h \ ${B}/qtui/smdprompt.base.h \ ${B}/qtui/offsetwin.base.h \ ${B}/qtui/pluginwin.base.h \ ${B}/qtui/statusbar.base.h \ ${B}/qtui/texturecoord.base.h \ ${B}/qtui/textwin.base.h \ ${B}/qtui/texwin.base.h \ ${B}/qtui/transformwin.base.h \ ${B}/qtui/valuewin.base.h \ ${B}/qtui/viewportsettings.base.h \ \ ${B}/qtui/resources.cpp MM3D_MOC= \ ${B}/implui/alignwin.moc.cc \ ${B}/implui/aboutwin.moc.cc \ ${B}/implui/animconvertwin.moc.cc \ ${B}/implui/animeditwin.moc.cc \ ${B}/implui/animexportwin.moc.cc \ ${B}/implui/animsetwin.moc.cc \ ${B}/implui/animwidget.moc.cc \ ${B}/implui/animwin.moc.cc \ ${B}/implui/autoassignjointwin.moc.cc \ ${B}/implui/backgroundselect.moc.cc \ ${B}/implui/backgroundwin.moc.cc \ ${B}/implui/boolpanel.moc.cc \ ${B}/implui/boolwin.moc.cc \ ${B}/implui/contextinfluences.moc.cc \ ${B}/implui/contextname.moc.cc \ ${B}/implui/contextpanel.moc.cc \ ${B}/implui/contextposition.moc.cc \ ${B}/implui/contextprojection.moc.cc \ ${B}/implui/contextrotation.moc.cc \ ${B}/tools/cubetoolwidget.moc.cc \ ${B}/tools/cylindertoolwidget.moc.cc \ ${B}/tools/ellipsetoolwidget.moc.cc \ ${B}/depui/errorobj.moc.cc \ ${B}/implui/extrudewin.moc.cc \ ${B}/implui/groupclean.moc.cc \ ${B}/implui/groupwin.moc.cc \ ${B}/implui/helpwin.moc.cc \ ${B}/implui/jointwin.moc.cc \ ${B}/implui/painttexturewin.moc.cc \ ${B}/implui/pointwin.moc.cc \ ${B}/implui/projectionwin.moc.cc \ ${B}/implui/keyvaluewin.moc.cc \ ${B}/implui/licensewin.moc.cc \ ${B}/implui/mergewin.moc.cc \ ${B}/implui/metawin.moc.cc \ ${B}/implui/mview.moc.cc \ ${B}/depui/modelviewport.moc.cc \ ${B}/implui/newanim.moc.cc \ ${B}/implui/cal3dprompt.moc.cc \ ${B}/implui/iqeprompt.moc.cc \ ${B}/implui/md3prompt.moc.cc \ ${B}/implui/ms3dprompt.moc.cc \ ${B}/implui/objprompt.moc.cc \ ${B}/implui/smdprompt.moc.cc \ ${B}/implui/offsetwin.moc.cc \ ${B}/implui/pluginwin.moc.cc \ ${B}/tools/polytoolwidget.moc.cc \ ${B}/tools/projtoolwidget.moc.cc \ ${B}/tools/scaletoolwidget.moc.cc \ ${B}/tools/selectfacetoolwidget.moc.cc \ ${B}/implui/spherifywin.moc.cc \ ${B}/implui/statusbar.moc.cc \ ${B}/depui/textureframe.moc.cc \ ${B}/implui/texturecoord.moc.cc \ ${B}/depui/texwidget.moc.cc \ ${B}/implui/texwin.moc.cc \ ${B}/implui/transformwin.moc.cc \ ${B}/implui/valuewin.moc.cc \ ${B}/implui/viewportsettings.moc.cc \ ${B}/implui/viewpanel.moc.cc \ ${B}/implui/viewwin.moc.cc \ MM3D_MOC_OBJ= \ ${B}/implui/alignwin.moc.o \ ${B}/implui/aboutwin.moc.o \ ${B}/implui/animconvertwin.moc.o \ ${B}/implui/animeditwin.moc.o \ ${B}/implui/animexportwin.moc.o \ ${B}/implui/animsetwin.moc.o \ ${B}/implui/animwidget.moc.o \ ${B}/implui/animwin.moc.o \ ${B}/implui/autoassignjointwin.moc.o \ ${B}/implui/backgroundselect.moc.o \ ${B}/implui/backgroundwin.moc.o \ ${B}/implui/boolpanel.moc.o \ ${B}/implui/boolwin.moc.o \ ${B}/implui/contextgroup.moc.o \ ${B}/implui/contextinfluences.moc.o \ ${B}/implui/contextname.moc.o \ ${B}/implui/contextpanel.moc.o \ ${B}/implui/contextposition.moc.o \ ${B}/implui/contextprojection.moc.o \ ${B}/implui/contextrotation.moc.o \ ${B}/tools/cubetoolwidget.moc.o \ ${B}/tools/cylindertoolwidget.moc.o \ ${B}/tools/ellipsetoolwidget.moc.o \ ${B}/tools/rotatetoolwidget.moc.o \ ${B}/tools/torustoolwidget.moc.o \ ${B}/tools/toolwidget.moc.o \ ${B}/depui/errorobj.moc.o \ ${B}/implui/extrudewin.moc.o \ ${B}/implui/groupclean.moc.o \ ${B}/implui/groupwin.moc.o \ ${B}/implui/helpwin.moc.o \ ${B}/implui/jointwin.moc.o \ ${B}/implui/painttexturewin.moc.o \ ${B}/implui/pointwin.moc.o \ ${B}/implui/projectionwin.moc.o \ ${B}/implui/keyvaluewin.moc.o \ ${B}/implui/licensewin.moc.o \ ${B}/implui/mapdirection.moc.o \ ${B}/implui/mergewin.moc.o \ ${B}/implui/metawin.moc.o \ ${B}/implui/mview.moc.o \ ${B}/depui/modelviewport.moc.o \ ${B}/implui/newanim.moc.o \ ${B}/implui/cal3dprompt.moc.o \ ${B}/implui/iqeprompt.moc.o \ ${B}/implui/md3prompt.moc.o \ ${B}/implui/ms3dprompt.moc.o \ ${B}/implui/objprompt.moc.o \ ${B}/implui/smdprompt.moc.o \ ${B}/implui/offsetwin.moc.o \ ${B}/implui/pluginwin.moc.o \ ${B}/tools/polytoolwidget.moc.o \ ${B}/tools/projtoolwidget.moc.o \ ${B}/tools/scaletoolwidget.moc.o \ ${B}/tools/selectfacetoolwidget.moc.o \ ${B}/implui/spherifywin.moc.o \ ${B}/implui/statusbar.moc.o \ ${B}/depui/textureframe.moc.o \ ${B}/implui/texturecoord.moc.o \ ${B}/depui/texwidget.moc.o \ ${B}/implui/texwin.moc.o \ ${B}/implui/transformwin.moc.o \ ${B}/implui/valuewin.moc.o \ ${B}/implui/viewportsettings.moc.o \ ${B}/implui/viewpanel.moc.o \ ${B}/implui/viewwin.moc.o \ MM3D_OBJ= \ ${B}/src/3dm.o \ ${B}/mm3dcore/3dmprefs.o \ ${B}/implui/aboutwin.o \ ${B}/mm3dcore/align.o \ ${B}/commands/aligncmd.o \ ${B}/implui/alignwin.o \ ${B}/mm3dcore/allocstats.o \ ${B}/implui/animconvertwin.o \ ${B}/implui/animeditwin.o \ ${B}/implui/animexportwin.o \ ${B}/implui/animsetwin.o \ ${B}/implui/animwidget.o \ ${B}/implui/animwin.o \ ${B}/tools/atrfartool.o \ ${B}/tools/atrneartool.o \ ${B}/implui/autoassignjointwin.o \ ${B}/implui/backgroundselect.o \ ${B}/implui/backgroundwin.o \ ${B}/implui/boolpanel.o \ ${B}/implui/boolwin.o \ ${B}/implui/contextgroup.o \ ${B}/implui/contextinfluences.o \ ${B}/implui/contextname.o \ ${B}/implui/contextpanel.o \ ${B}/implui/contextposition.o \ ${B}/implui/contextprojection.o \ ${B}/implui/contextrotation.o \ ${B}/mm3dcore/contextwidget.o \ ${B}/mm3dcore/contextpanelobserver.o \ ${B}/tools/bgmovetool.o \ ${B}/tools/bgscaletool.o \ ${B}/mm3dcore/bounding.o \ ${B}/mm3dcore/cmdline.o \ ${B}/mm3dcore/cmdmgr.o \ ${B}/mm3dcore/command.o \ ${B}/commands/capcmd.o \ ${B}/commands/copycmd.o \ ${B}/tools/cubetool.o \ ${B}/tools/cubetoolwidget.o \ ${B}/tools/cylindertool.o \ ${B}/tools/cylindertoolwidget.o \ ${B}/mm3dcore/decal.o \ ${B}/mm3dcore/decalmgr.o \ ${B}/commands/deletecmd.o \ ${B}/commands/dupcmd.o \ ${B}/commands/edgedivcmd.o \ ${B}/commands/edgeturncmd.o \ ${B}/tools/ellipsetool.o \ ${B}/tools/ellipsetoolwidget.o \ ${B}/tools/extrudetool.o \ ${B}/depui/errorobj.o \ ${B}/commands/extrudecmd.o \ ${B}/implui/extrudewin.o \ ${B}/commands/faceoutcmd.o \ ${B}/commands/flattencmd.o \ ${B}/commands/flipcmd.o \ ${B}/implui/groupclean.o \ ${B}/implui/groupwin.o \ ${B}/implui/helpwin.o \ ${B}/commands/hidecmd.o \ ${B}/commands/invertcmd.o \ ${B}/commands/invnormalcmd.o \ ${B}/tools/jointtool.o \ ${B}/tools/pointtool.o \ ${B}/tools/projtool.o \ ${B}/implui/jointwin.o \ ${B}/implui/painttexturewin.o \ ${B}/implui/pointwin.o \ ${B}/implui/projectionwin.o \ ${B}/implui/keycfg.o \ ${B}/implui/keyvaluewin.o \ ${B}/implui/licensewin.o \ ${B}/mm3dcore/luaif.o \ ${B}/mm3dcore/luascript.o \ ${B}/commands/makefacecmd.o \ ${B}/implui/mapdirection.o \ ${B}/implui/mergewin.o \ ${B}/implui/metawin.o \ ${B}/tools/movetool.o \ ${B}/implui/msgqt.o \ ${B}/implui/mview.o \ ${B}/depui/modelviewport.o \ ${B}/commands/pastecmd.o \ ${B}/mm3dcore/pluginmgr.o \ ${B}/implui/newanim.o \ ${B}/implui/cal3dprompt.o \ ${B}/implui/iqeprompt.o \ ${B}/implui/md3prompt.o \ ${B}/implui/ms3dprompt.o \ ${B}/implui/objprompt.o \ ${B}/implui/smdprompt.o \ ${B}/implui/pluginwin.o \ ${B}/commands/offsetcmd.o \ ${B}/implui/offsetwin.o \ ${B}/tools/polytool.o \ ${B}/tools/polytoolwidget.o \ ${B}/tools/projtoolwidget.o \ ${B}/mm3dcore/prefparse.o \ ${B}/mm3dcore/prefs.o \ ${B}/implui/qtmain.o \ ${B}/implui/qttex.o \ ${B}/tools/rectangletool.o \ ${B}/qtui/resources.o \ ${B}/mm3dcore/rotatepoint.o \ ${B}/commands/rotatetexcmd.o \ ${B}/tools/rotatetool.o \ ${B}/tools/scaletool.o \ ${B}/tools/scaletoolwidget.o \ ${B}/tools/selectfacetoolwidget.o \ ${B}/mm3dcore/scriptif.o \ ${B}/tools/selectbonetool.o \ ${B}/tools/selectpointtool.o \ ${B}/tools/selectprojtool.o \ ${B}/tools/selectconnectedtool.o \ ${B}/tools/selectfacetool.o \ ${B}/tools/selectgrouptool.o \ ${B}/tools/selectvertextool.o \ ${B}/tools/sheartool.o \ ${B}/commands/selectfreecmd.o \ ${B}/commands/simplifycmd.o \ ${B}/commands/snapcmd.o \ ${B}/commands/spherifycmd.o \ ${B}/implui/spherifywin.o \ ${B}/implui/statusbar.o \ ${B}/src/stdcmds.o \ ${B}/src/stdfilters.o \ ${B}/src/stdtexfilters.o \ ${B}/src/stdtools.o \ ${B}/commands/subdividecmd.o \ ${B}/mm3dcore/sysconf.o \ ${B}/depui/textureframe.o \ ${B}/implui/texturecoord.o \ ${B}/mm3dcore/texturetest.o \ ${B}/depui/texwidget.o \ ${B}/implui/texwin.o \ ${B}/implui/transformwin.o \ ${B}/implui/transimp.o \ ${B}/libmm3d/translate.o \ ${B}/mm3dcore/tool.o \ ${B}/mm3dcore/toolbox.o \ ${B}/mm3dcore/toolpoly.o \ ${B}/tools/rotatetoolwidget.o \ ${B}/tools/torustool.o \ ${B}/tools/torustoolwidget.o \ ${B}/tools/toolwidget.o \ ${B}/commands/unweldcmd.o \ ${B}/implui/valuewin.o \ ${B}/tools/vertextool.o \ ${B}/tools/dragvertextool.o \ ${B}/implui/viewportsettings.o \ ${B}/implui/viewpanel.o \ ${B}/implui/viewwin.o \ ${B}/implui/viewwin_influences.o \ ${B}/commands/weldcmd.o QMFILES = \ ${B}/translations/mm3d_de.qm \ ${B}/translations/mm3d_fr.qm \ ${B}/translations/mm3d_sk.qm \ ${B}/translations/mm3d_ref.qm \ ${B}/translations/mm3d_bork.qm MM3D_HTML = \ ${B}/doc/html/olh_detailsindex.html \ ${B}/doc/html/olh_vertexdetails.html \ ${B}/doc/html/olh_facedetails.html \ ${B}/doc/html/olh_meshdetails.html \ ${B}/doc/html/olh_normaldetails.html \ ${B}/doc/html/olh_groupdetails.html \ ${B}/doc/html/olh_materialdetails.html \ ${B}/doc/html/olh_texturecoorddetails.html \ ${B}/doc/html/olh_projectiondetails.html \ ${B}/doc/html/olh_jointdetails.html \ ${B}/doc/html/olh_pointdetails.html \ ${B}/doc/html/olh_animationdetails.html \ ${B}/doc/html/olh_backgroundimagedetails.html \ ${B}/doc/html/olh_alignwin.html \ ${B}/doc/html/olh_animconvertwin.html \ ${B}/doc/html/olh_animexportwin.html \ ${B}/doc/html/olh_animsetwin.html \ ${B}/doc/html/olh_animwin.html \ ${B}/doc/html/olh_autoassignjointwin.html \ ${B}/doc/html/olh_backgroundwin.html \ ${B}/doc/html/olh_boolwin.html \ ${B}/doc/html/olh_cmdline.html \ ${B}/doc/html/olh_commands.html \ ${B}/doc/html/olh_develop.html \ ${B}/doc/html/olh_quakemd2.html \ ${B}/doc/html/olh_quakemd3.html \ ${B}/doc/html/olh_cal3d.html \ ${B}/doc/html/olh_groupwin.html \ ${B}/doc/html/olh_index.html \ ${B}/doc/html/olh_influences.html \ ${B}/doc/html/olh_jointwin.html \ ${B}/doc/html/olh_license.html \ ${B}/doc/html/olh_mergewin.html \ ${B}/doc/html/olh_metawin.html \ ${B}/doc/html/olh_mm3dformat.html \ ${B}/doc/html/olh_cal3dprompt.html \ ${B}/doc/html/olh_ms3dprompt.html \ ${B}/doc/html/olh_objprompt.html \ ${B}/doc/html/olh_iqeprompt.html \ ${B}/doc/html/olh_quakemd3prompt.html \ ${B}/doc/html/olh_smdprompt.html \ ${B}/doc/html/olh_offsetwin.html \ ${B}/doc/html/olh_painttexturewin.html \ ${B}/doc/html/olh_pointwin.html \ ${B}/doc/html/olh_pluginwin.html \ ${B}/doc/html/olh_projectionwin.html \ ${B}/doc/html/olh_properties.html \ ${B}/doc/html/olh_spherifywin.html \ ${B}/doc/html/olh_texturecoordwin.html \ ${B}/doc/html/olh_texturewin.html \ ${B}/doc/html/olh_tips.html \ ${B}/doc/html/olh_tools.html \ ${B}/doc/html/olh_tutorial_index.html \ ${B}/doc/html/olh_transformwin.html \ ${B}/doc/html/olh_viewportsettings.html \ ${B}/doc/html/olh_whatsnew.html \ ${B}/doc/html/olh_mainwin.html ifeq (${V},1) echo_cmd=@: Q= else echo_cmd=@echo Q=@ endif define DO_CC $(echo_cmd) "CC $<" $(Q)${CC} ${DEFS} -c $< -o $@ ${CCFLAGS} endef define DO_TOOLS_CC $(echo_cmd) "TOOLS CC $<" $(Q)${TOOLS_CC} -c $< -o $@ ${TOOLS_CCFLAGS} endef define DO_CXX $(echo_cmd) "CXX $<" $(Q)${CXX} ${DEFS} -c $< -o $@ ${CXXFLAGS} endef define DO_UIC $(echo_cmd) "UIC $<" $(Q)${UIC} $< -o $@ endef define DO_MOC $(echo_cmd) "MOC $<" $(Q)${MOC} $< -o $@ endef define DO_LRELEASE $(echo_cmd) "LRELEASE $<" $(Q)${LRELEASE} $< -qm $@ endef define DO_HTML $(echo_cmd) "HTML $<" $(Q)${B_TOOLS}/util/hpagemake${BINEXT} $< > $@ endef .PHONY: all makedirs mm3d_ui mm3d_moc mm3d qmfiles hpagemake docs install deploy installer clean all: makedirs mm3d_ui mm3d_moc mm3d qmfiles hpagemake docs # mingw.org's mingw32-make.exe uses Window's command processing and mkdir makedirs: ifdef MINGW32_MAKE @IF NOT EXIST "${BUILD}\${BUILDSUB}" ${MKDIR} "${BUILD}\${BUILDSUB}" @IF NOT EXIST "${BUILD}\${BUILDSUB}\src" ${MKDIR} "${BUILD}\${BUILDSUB}\src" @IF NOT EXIST "${BUILD}\${BUILDSUB}\depui" ${MKDIR} "${BUILD}\${BUILDSUB}\depui" @IF NOT EXIST "${BUILD}\${BUILDSUB}\implui" ${MKDIR} "${BUILD}\${BUILDSUB}\implui" @IF NOT EXIST "${BUILD}\${BUILDSUB}\qtui" ${MKDIR} "${BUILD}\${BUILDSUB}\qtui" @IF NOT EXIST "${BUILD}\${BUILDSUB}\tools" ${MKDIR} "${BUILD}\${BUILDSUB}\tools" @IF NOT EXIST "${BUILD}\${BUILDSUB}\libmm3d" ${MKDIR} "${BUILD}\${BUILDSUB}\libmm3d" @IF NOT EXIST "${BUILD}\${BUILDSUB}\mm3dcore" ${MKDIR} "${BUILD}\${BUILDSUB}\mm3dcore" @IF NOT EXIST "${BUILD}\${BUILDSUB}\commands" ${MKDIR} "${BUILD}\${BUILDSUB}\commands" @IF NOT EXIST "${BUILD}\${BUILDSUB}\translations" ${MKDIR} "${BUILD}\${BUILDSUB}\translations" @IF NOT EXIST "${BUILD}\${BUILDSUB}\doc" ${MKDIR} "${BUILD}\${BUILDSUB}\doc" @IF NOT EXIST "${BUILD}\${BUILDSUB}\doc\html" ${MKDIR} "${BUILD}\${BUILDSUB}\doc\html" @IF NOT EXIST "${BUILD}\${BUILDSUB_TOOLS}" ${MKDIR} "${BUILD}\${BUILDSUB_TOOLS}" @IF NOT EXIST "${BUILD}\${BUILDSUB_TOOLS}\util" ${MKDIR} "${BUILD}\${BUILDSUB_TOOLS}\util" else @${MKDIR} "${B}" @${MKDIR} "${B}/src" @${MKDIR} "${B}/depui" @${MKDIR} "${B}/implui" @${MKDIR} "${B}/qtui" @${MKDIR} "${B}/tools" @${MKDIR} "${B}/libmm3d" @${MKDIR} "${B}/mm3dcore" @${MKDIR} "${B}/commands" @${MKDIR} "${B}/translations" @${MKDIR} "${B}/doc" @${MKDIR} "${B}/doc/html" @${MKDIR} "${B_TOOLS}" @${MKDIR} "${B_TOOLS}/util" endif mm3d_ui: ${MM3D_UI} mm3d_moc: ${MM3D_MOC} qmfiles: ${QMFILES} ${B_TOOLS}/util/hpagemake${BINEXT}: ${B_TOOLS}/util/hpagemake.o $(Q)${TOOLS_CC} ${TOOLS_CCFLAGS} -o ${B_TOOLS}/util/hpagemake${BINEXT} ${B_TOOLS}/util/hpagemake.o hpagemake: ${B_TOOLS}/util/hpagemake${BINEXT} docs: hpagemake ${MM3D_HTML} $(BIN)/mm3d${BINEXT}: ${MM3D_MOC_OBJ} ${LIB_OBJ} ${MM3D_OBJ} $(echo_cmd) "LINKING $(BIN)/mm3d${BINEXT}" ifdef MINGW ifdef MINGW32_MAKE $(Q)${WINDRES} --preprocessor="$(CC) -E -xc-header -DRC_INVOKED" src/win_resource.rc ${B}/src/win_resource.o else $(Q)${WINDRES} src/win_resource.rc ${B}/src/win_resource.o endif $(Q)${CXX} ${CXXFLAGS} ${DEFS} -o $(BIN)/mm3d${BINEXT} ${MM3D_OBJ} ${MM3D_MOC_OBJ} ${LIB_OBJ} ${B}/src/win_resource.o ${LINK} else $(Q)${CXX} ${CXXFLAGS} ${DEFS} -o $(BIN)/mm3d${BINEXT} ${MM3D_OBJ} ${MM3D_MOC_OBJ} ${LIB_OBJ} ${LINK} endif ifdef STRIP $(echo_cmd) "STRIP $(BIN)/mm3d${BINEXT}" $(Q)$(STRIP) $(BIN)/mm3d${BINEXT} endif mm3d: $(BIN)/mm3d${BINEXT} # make install doesn't work for mingw32-make.exe because it's case insenitive and autotools requires INSTALL file. install: deploy deploy: all ifdef MINGW ifdef MINGW32_MAKE @IF NOT EXIST "${BUILD}\${BUILDSUB}\install" ${MKDIR} "${BUILD}\${BUILDSUB}\install" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\doc" ${MKDIR} "${BUILD}\${BUILDSUB}\install\doc" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\doc\html" ${MKDIR} "${BUILD}\${BUILDSUB}\install\doc\html" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\doc\html\olh_images" ${MKDIR} "${BUILD}\${BUILDSUB}\install\doc\html\olh_images\screencaps" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\doc\html\olh_images\screencaps" ${MKDIR} "${BUILD}\${BUILDSUB}\install\doc\html\olh_images" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\doc\html\olh_images\tools" ${MKDIR} "${BUILD}\${BUILDSUB}\install\doc\html\olh_images\tools" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\translations" ${MKDIR} "${BUILD}\${BUILDSUB}\install\translations" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\plugins" ${MKDIR} "${BUILD}\${BUILDSUB}\install\plugins" @IF NOT EXIST "${BUILD}\${BUILDSUB}\install\plugins\1.3" ${MKDIR} "${BUILD}\${BUILDSUB}\install\plugins\1.3" @copy "$(BUILD)\$(BUILDSUB)\mm3d${BINEXT}" "$(BUILD)\$(BUILDSUB)\install" @copy "$(BUILD)\$(BUILDSUB)\doc\html\*.html" "$(BUILD)\$(BUILDSUB)\install\doc\html" @copy "doc\html\olh_images\screencaps\*.png" "$(BUILD)\$(BUILDSUB)\install\doc\html\olh_images\screencaps" @copy "doc\html\olh_images\tools\*.png" "$(BUILD)\$(BUILDSUB)\install\doc\html\olh_images\tools" @copy "doc\html\olh_images\tools\*.jpg" "$(BUILD)\$(BUILDSUB)\install\doc\html\olh_images\tools" @copy "$(BUILD)\$(BUILDSUB)\translations\*.qm" "$(BUILD)\$(BUILDSUB)\install\translations" @$(WINDEPLOYQT) "$(BUILD)\$(BUILDSUB)\install" else @$(MKDIR) "$(B)/install" @$(MKDIR) "$(B)/install/doc/html" @$(MKDIR) "$(B)/install/doc/html/olh_images/screencaps" @$(MKDIR) "$(B)/install/doc/html/olh_images/tools" @$(MKDIR) "$(B)/install/translations" @$(MKDIR) "$(B)/install/plugins/1.3" @cp "$(B)/mm3d${BINEXT}" "$(B)/install" @cp $(QMFILES) "$(B)/install/translations" @cp $(MM3D_HTML) "$(B)/install/doc/html" @cp doc/html/olh_images/screencaps/*.png "$(B)/install/doc/html/olh_images/screencaps" @cp doc/html/olh_images/tools/*.png "$(B)/install/doc/html/olh_images/tools" @cp doc/html/olh_images/tools/*.jpg "$(B)/install/doc/html/olh_images/tools" @$(WINDEPLOYQT) "$(B)/install" endif else $(INSTALL) -d "$(PREFIX)/bin" $(INSTALL) -m 0755 $(BIN)/mm3d${BINEXT} "$(PREFIX)/bin/mm3d" $(INSTALL) -d "$(PREFIX)/share/mm3d/translations/" ${INSTALL} -m 0644 $(QMFILES) "$(PREFIX)/share/mm3d/translations/" $(INSTALL) -d "$(PREFIX)/share/applications/" $(INSTALL) -d "$(PREFIX)/share/metainfo/" $(INSTALL) -d "$(PREFIX)/share/mime/packages/" $(INSTALL) -d "$(PREFIX)/share/icons/hicolor/128x128/apps/" $(INSTALL) -d "$(PREFIX)/share/icons/hicolor/64x64/apps/" $(INSTALL) -d "$(PREFIX)/share/icons/hicolor/32x32/apps/" $(INSTALL) -d "$(PREFIX)/share/icons/hicolor/16x16/apps/" ${INSTALL} -m 0644 desktop/moe.clover.mm3d.desktop "$(PREFIX)/share/applications/" ${INSTALL} -m 0644 desktop/moe.clover.mm3d.metainfo.xml "$(PREFIX)/share/metainfo/" ${INSTALL} -m 0644 desktop/moe.clover.mm3d.mm3dmodel.xml "$(PREFIX)/share/mime/packages/" ${INSTALL} -m 0644 desktop/moe.clover.mm3d.png "$(PREFIX)/share/icons/hicolor/128x128/apps/moe.clover.mm3d.png" ${INSTALL} -m 0644 desktop/moe.clover.mm3d-64.png "$(PREFIX)/share/icons/hicolor/64x64/apps/moe.clover.mm3d.png" ${INSTALL} -m 0644 desktop/moe.clover.mm3d-32.png "$(PREFIX)/share/icons/hicolor/32x32/apps/moe.clover.mm3d.png" ${INSTALL} -m 0644 desktop/moe.clover.mm3d-16.png "$(PREFIX)/share/icons/hicolor/16x16/apps/moe.clover.mm3d.png" $(INSTALL) -d "$(PREFIX)/share/man/man1/" ${INSTALL} -m 0644 man/mm3d.1 "$(PREFIX)/share/man/man1/" $(INSTALL) -d "$(PREFIX)/share/mm3d/plugins/1.3" $(INSTALL) -d "$(PREFIX)/share/doc/mm3d/html/" $(INSTALL) -d "$(PREFIX)/share/doc/mm3d/html/olh_images/screencaps/" $(INSTALL) -d "$(PREFIX)/share/doc/mm3d/html/olh_images/tools/" ${INSTALL} -m 0644 $(MM3D_HTML) "$(PREFIX)/share/doc/mm3d/html/" ${INSTALL} -m 0644 doc/html/olh_images/screencaps/*.png "$(PREFIX)/share/doc/mm3d/html/olh_images/screencaps/" ${INSTALL} -m 0644 doc/html/olh_images/tools/*.png "$(PREFIX)/share/doc/mm3d/html/olh_images/tools/" ${INSTALL} -m 0644 doc/html/olh_images/tools/*.jpg "$(PREFIX)/share/doc/mm3d/html/olh_images/tools/" endif installer: deploy ifdef MINGW makensis mm3d-win32-installer.nsi endif # # macOS AppBundle # MAC_APP = $(B)/Maverick Model 3D.app define INFOPLIST CFBundleDevelopmentRegion en UTExportedTypeDeclarations UTTypeConformsTo public.data UTTypeIdentifier moe.clover.mm3dmodel UTTypeDescription MM3D model UTTypeIconFile mm3d UTTypeReferenceURL https://clover.moe/mm3d_manual/olh_mm3dformat.html UTTypeTagSpecification public.filename-extension mm3d CFBundleDocumentTypes CFBundleTypeName MM3D model LSItemContentTypes moe.clover.mm3dmodel CFBundleTypeRole Editor LSHandlerRank Owner CFBundleTypeName Quake 2 model CFBundleTypeExtensions md2 CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleTypeName Quake 3 model CFBundleTypeExtensions md3 CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleTypeName Milkshape 3D model CFBundleTypeExtensions ms3d CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleTypeName Wavefront OBJ model CFBundleTypeExtensions obj CFBundleTypeRole Editor LSHandlerRank Alternate CFBundleExecutable mm3d CFBundleIconFile mm3d CFBundleIdentifier moe.clover.mm3d CFBundleInfoDictionaryVersion 6.0 CFBundleName Maverick Model 3D CFBundlePackageType APPL CFBundleShortVersionString 1.3.15 CFBundleSignature ???? CFBundleVersion 1.3.15 CGDisableCoalescedUpdates LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSHumanReadableCopyright Copyright © 2004-2008 Kevin Worcester, Copyright © 2009-2024 Zack Middleton. NSPrincipalClass NSApplication endef export INFOPLIST define LOCVERSIONPLIST LprojCompatibleVersion 123 LprojLocale XXLANGXX LprojRevisionLevel 1 LprojVersion 123 endef export LOCVERSIONPLIST appbundle: all ${INSTALL} -d "$(MAC_APP)/Contents/MacOS/" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/" ${INSTALL} -d "$(MAC_APP)/Contents/PlugIns/mm3d/1.3" ${INSTALL} -d "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/screencaps/" ${INSTALL} -d "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/tools/" ${INSTALL} -d "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/" ${INSTALL} -m 0755 "$(BIN)/mm3d${BINEXT}" "$(MAC_APP)/Contents/MacOS/mm3d" ${INSTALL} -m 0644 mm3d.icns "$(MAC_APP)/Contents/Resources/mm3d.icns" ${INSTALL} -m 0644 $(MM3D_HTML) "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/" ${INSTALL} -m 0644 doc/html/olh_images/screencaps/*.png "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/screencaps/" ${INSTALL} -m 0644 doc/html/olh_images/tools/*.png "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/tools/" ${INSTALL} -m 0644 doc/html/olh_images/tools/*.jpg "$(MAC_APP)/Contents/SharedSupport/mm3d/doc/html/olh_images/tools/" ${INSTALL} -m 0644 $(QMFILES) "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/" echo "APPL????" > "$(MAC_APP)/Contents/PkgInfo" @echo "$$INFOPLIST" > "$(MAC_APP)/Contents/Info.plist" @echo "Wrote \"$(MAC_APP)/Contents/Info.plist\"" $(MACDEPLOYQT) "$(MAC_APP)" @# Add translations of Qt itself $(LCONVERT) -o "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/qt_de.qm" "$(QT_TRANSLATIONS_DIR)/qtbase_de.qm" $(LCONVERT) -o "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/qt_fr.qm" "$(QT_TRANSLATIONS_DIR)/qtbase_fr.qm" $(LCONVERT) -o "$(MAC_APP)/Contents/SharedSupport/mm3d/translations/qt_sk.qm" "$(QT_TRANSLATIONS_DIR)/qtbase_sk.qm" @# Tell macOS to translate builtin dialogs (i.e., file chooser) ${INSTALL} -d "$(MAC_APP)/Contents/Resources/en.lproj" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/de.lproj" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/fr.lproj" ${INSTALL} -d "$(MAC_APP)/Contents/Resources/sk.lproj" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/en/g" > "$(MAC_APP)/Contents/Resources/en.lproj/locversion.plist" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/de/g" > "$(MAC_APP)/Contents/Resources/de.lproj/locversion.plist" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/fr/g" > "$(MAC_APP)/Contents/Resources/fr.lproj/locversion.plist" @echo "$$LOCVERSIONPLIST" | sed "s/XXLANGXX/sk/g" > "$(MAC_APP)/Contents/Resources/sk.lproj/locversion.plist" ${B}/%.o: src/%.c $(DO_CC) ${B_TOOLS}/util/%.o: util/%.c $(DO_TOOLS_CC) ${B}/%.o: src/%.cc $(DO_CXX) ${B}/src/%.o: src/%.cc $(DO_CXX) ${B}/qtui/%.base.h: src/qtui/%.ui $(DO_UIC) ${B}/implui/%.moc.cc: src/implui/%.h $(DO_MOC) ${B}/tools/%.moc.cc: src/tools/%.h $(DO_MOC) ${B}/depui/%.moc.cc: src/depui/%.h $(DO_MOC) ${B}/%.moc.o: ${B}/%.moc.cc $(DO_CXX) ${B}/qtui/resources.cpp: src/qtui/resources.qrc src/qtui/images/zoomin.xpm src/qtui/images/zoomout.xpm $(echo_cmd) "RCC $<" $(Q)${RCC} --name qtuiResources $< -o $@ ${B}/qtui/resources.o: ${B}/qtui/resources.cpp $(DO_CXX) ${B}/translations/%.qm: translations/%.ts $(DO_LRELEASE) ${B}/doc/html/%.html: doc/html/%.page doc/html/%.htm doc/html/template.htm ${B_TOOLS}/util/hpagemake${BINEXT} $(DO_HTML) # Include build dependencies (.d files). OBJ=${LIB_OBJ} ${MM3D_OBJ} ${MM3D_MOC_OBJ} OBJ_D_FILES=$(filter %.d,$(OBJ:%.o=%.d)) -include $(OBJ_D_FILES) clean: $(Q)rm -f ${OBJ_D_FILES} $(Q)rm -f ${LIB_OBJ} $(Q)rm -f ${MM3D_UI} $(Q)rm -f ${MM3D_MOC} $(Q)rm -f ${QMFILES} $(Q)rm -f ${MM3D_OBJ} $(Q)rm -f ${MM3D_MOC_OBJ} $(Q)rm -f ${MM3D_HTML} $(Q)rm -f $(BIN)/mm3d${BINEXT} mm3d-1.3.15/NEWS000066400000000000000000000005231466047437300131560ustar00rootroot00000000000000This is not the NEWS you're looking for. See README for general information See INSTALL for installation instructions See INSTALL.WIN32 for build and install instructions for Windows See COPYING for license (GPL) See ChangeLog for a list of changes since the last version See AUTHORS to find out who is to blame for this software mm3d-1.3.15/README.SVN000066400000000000000000000034541466047437300140120ustar00rootroot00000000000000If you're seeing this file it is probably because you used Subversion to get the source code for this program. If that is the case you may be wondering how to build it (since there isn't a ./configure script to run). The ./configure script and many other files are output files from an early stage of the build process. Those output files are not included in source control, you must build them from the sources. Additionally the translation files and help pages are also output files from the build process. You can build mm3d without building the translations and doc/html files. For instructions on creating the build files for MM3D, translations, and help, continue reading below. If you plan to look at or modify the code itself, read the CODE file in this directory may be helpful. To build MM3D: Run the ./autogen.sh script. This runs autoconf and automake. If those run successfully you will have a ./configure script you can run to start the normal build process. To start the normal build process, see the INSTALL file. If you have problems building the translation files and documentation html files, you can just run "make" from the src/ directory instead of the top level trunk directory. To build translations files: You must have Qt's lrelease program in your path. If this is in your path you should be able to just run "make" in the translations directory. You do not need to build translations files to get an mm3d executable. If you get an error that the shell can't find the lrelease program and you don't care about translation files, then you can safely ignore this error. To build doc/html help files: The help files in doc/html are source files that use templates to build the final help pages that appear in the mm3d help window. To build these final html pages just run "make" in the doc directory. mm3d-1.3.15/README.md000066400000000000000000000044521466047437300137430ustar00rootroot00000000000000Maverick Model 3D ----------------- Maverick Model 3D is a 3D model editor. It was written and tested on Linux. It is reported to run on other Unix-like operating systems. It also runs on Windows. It is based on [Misfit Model 3D](http://www.misfitcode.com/misfitmodel3d/) that was developed by Kevin Worcester from 2004 to 2009. Maverick Model 3D is maintained by Zack Middleton (zturtleman). The home page is here: https://clover.moe/mm3d Maverick Model 3D requires Qt (5.x) and OpenGL support. See the INSTALL file for details on where to get these packages. This program uses autoconf and automake for building from source. What this means is that if you are lucky you can install this program with these easy steps: ./autogen.sh ./configure make sudo make install This will build a 'mm3d' executable and install it in /usr/local/bin. Documentation will be in /usr/local/share/doc/mm3d. For more detailed installation instructions, see the INSTALL file. See INSTALL.WIN32 for Windows-specific instructions. ## Debian/Ubuntu Build dependencies: sudo apt install autoconf automake make gcc g++ qtbase5-dev qtbase5-dev-tools qttools5-dev-tools libgl1-mesa-dev qttranslations5-l10n can be used at run-time. ## Fedora Build dependencies: sudo dnf install autoconf automake make gcc gcc-c++ qt5-qtbase-devel qt5-linguist mesa-libGLU-devel qt5-qttranslations can be used at run-time. ## Arch Linux Build dependencies: sudo pacman -S autoconf automake make gcc qt5-base qt5-tools glu qt5-translations can be used at run-time. ## openSUSE Build dependencies: sudo zypper install autoconf automake make gcc libqt5-qtbase-devel libqt5-qttools glu-devel libqt5-qttranslations can be used at run-time. ## macOS To build a "Maverick Model 3D.app" AppBundle on macOS 10.14+, install homebrew from http://brew.sh and run the following commands. (I haven't tested these build steps as my MacBook Pro is limited to macOS 10.11 and Qt 5.12+ no longer supports it.) brew install autoconf automake qt@5 ./autogen.sh ./configure --with-Qt-dir=/usr/local/Cellar/qt@5/5.15.10_1 --with-macosx-version-min=10.13 make make appbundle --with-macosx-version-min should be set to the value of `QMAKE_MACOSX_DEPLOYMENT_TARGET` in /usr/local/Cellar/qt@5/5.15.10_1/mkspecs/common/macx.conf mm3d-1.3.15/TODO000066400000000000000000000102121466047437300131430ustar00rootroot00000000000000For 1.4: Test build with gcc 4.3 Finish doc TODO list Update plugin docs to point to User's 1.4 plugin directory ImTex compile fix For 1.6: Resample animation Change animation playback speed Bone Joints Interactive influence weight adjustment IK Considerations Fix relative paths when saving in a different directory Export/Import MM3D Geometry Materials Skeleton Animations Texture Projections Types Icosahedron Gradient textures Only list supported image formats in animation capture export Integrate libmisfit Make middle mouse button scroll zoom in on point under the mouse Delay normal calculation when manipulating a large number of vertices Fix MM3D data size problem (assume size instead of using read size) Embedded textures Implement embedded textures Extract embedded textures to external files Allow double-sided polygons Attract near/far Position decal 2D/3D Curve: linear/sin/log/quad Later: Warn user if some model properties will not be saved in specified format Format options: Model format-specific dialog box (like Save dialog) Must save in that format to get dialog Dialog always available (tabbed for each format?) Save format-specific options in MM3D format Options dialog can encode/decode Prompt at save time Only prompt for options on first save or on "Save As" Meta data context panel For meta data of any primitive Coloring on texture paint Adjust texture coordinates on turn edge? Adjust texture coordinates on split edge? On user path input, always replace backslashes with slashes MD3 Custom export dialog to select groups, tags, and animations Handle missing/out-of-order animations more sanely Center viewport on mouse position Point meta data? Check flipping on 3DS texture coordinates Import heightmap as "terrain" Transformation - scale translation keyframes MM3D Unsupported data Load/save unknown type info UI for unknown type info Cylinder/Sphere Mapping Projection Window Show test pattern Move pole on sphere? Move pole on icosahedron? Bone Joints Multiple root joints Allowing reparenting and re-rooting of bone structure will invalidate keyframe animations have to deal with parents defined after children Allow setting of other OpenGL options? Commands Increase poly count to smooth (see blender?) Text Force save of a specific type Report OpenGL status (supported options) Unified drawing code Consider using glVertexPointer, glInterleavedArrays, or other draw optimization Import/Export .X (DirectX) Lightwave Export 3DS AC3D Wings MD5 ASE Direct primitive property edit Scale UV Normal Tools Align to line Align to plane Select Select Edge Click repeatedly to cycle through faces under the mouse (Ctrl-click?) Sculpt tool (surface that pushes selected vertices) More complete plugin window Allow enabling/disabling of plugins Allow plugin initialization order changes? Apply heightmap to selected faces Attempt to correct bad data in mm3d load, and allow user to attempt to continue Probably never: Finish MenuManager Hooks for add/remove model elements (for plugins) Scripting Make copy of model Script directories grafted into menu Script debugger Selection Select All Created (or all of type) Bilinear autoscale on non-power-of-two textures Animation Add frame relative Tools Ruler Chamfered primitives Custom lighting Save viewport as JPEG Import/Export FBX SMD Gamestudio MDL Renderman Blender Maya ASCII Raw OpenGL (Triangle Vertices, Normals, and Texture Coords) Texture coordinates Map group should preserve aspect ratio mm3d-1.3.15/TRANSLATORS000066400000000000000000000002061466047437300142140ustar00rootroot00000000000000French (initial translation by unknown) German (initial translation by unknown) Tobias Kuehnhammer Slovak Dusan Halicky mm3d-1.3.15/acinclude.m4000066400000000000000000001436551466047437300146660ustar00rootroot00000000000000AC_DEFUN([KSW_IS_PROFILE], [ AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING(for profile) AC_ARG_ENABLE([profile], [ --enable-profile=yes/no/core Specify "yes" or "core" to enable profiling.]) is_profile=no PROFILE= CORE_PROFILE= if test x"$enable_profile" = xyes; then PROFILE=-pg CORE_PROFILE=-pg is_profile=yes elif test x"$enable_profile" = xcore; then CORE_PROFILE=-pg is_profile="yes (core)" fi AC_DEFINE( [PROFILE], [], [Define to include profiling information] ) AC_DEFINE( [CORE_PROFILE], [], [Define to include core profiling information] ) AC_MSG_RESULT($is_profile) ]) AC_DEFUN([KSW_IS_DEBUG], [ AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING(for debug) AC_ARG_ENABLE([debug], [ --enable-debug=yes/no/cov Specify "yes" to enable a debug build.]) is_debug=no enable_debug_save_COVFLAGS="${COVFLAGS}" enable_debug_save_COVLFLAGS="${COVLFLAGS}" enable_debug_save_CFLAGS="${CFLAGS}" enable_debug_save_CXXFLAGS="${CXXFLAGS}" enable_debug_save_LDFLAGS="${LDFLAGS}" if test x"$enable_debug" = xyes; then COVFLAGS="" COVLFLAGS="" COMMONFLAGS="-g -fPIC" CFLAGS="${COMMONFLAGS} ${CFLAGS}" CXXFLAGS="${COMMONFLAGS} -std=c++11 ${CXXFLAGS}" LDFLAGS="${LDFLAGS}" elif test x"$enable_debug" = xcov; then COVFLAGS="-coverage" COVLFLAGS="-lgcov" COMMONFLAGS="-g -fPIC" CFLAGS="${COMMONFLAGS} ${CFLAGS}" CXXFLAGS="${COMMONFLAGS} -std=c++11 ${CXXFLAGS}" LDFLAGS="${LDFLAGS}" is_debug=coverage else omit_frame= # FIXME?: Using -fomit-frame-pointer causes SEGFAULT at start up on macOS 10.11 using clang++. #if test x"${CORE_PROFILE}" = "x"; then # omit_frame="-fomit-frame-pointer" #fi COVFLAGS="" COVLFLAGS="" COMMONFLAGS="-O2 ${omit_frame} -fno-math-errno -fPIC" CFLAGS="${COMMONFLAGS} ${CFLAGS}" CXXFLAGS="${COMMONFLAGS} -std=c++11 ${CXXFLAGS}" LDFLAGS="${omit_frame} -fno-math-errno ${LDFLAGS}" fi AC_TRY_LINK([#include ], , [ dnl Yay! if test x"$enable_debug" = xyes; then AC_DEFINE( [CODE_DEBUG], [], [Define to include debugging information] ) is_debug=yes fi ], [ dnl Boo! if test x"$enable_debug" != xyes; then COVFLAGS="${enable_debug_save_COVFLAGS}" COVLFLAGS="${enable_debug_save_COVLFLAGS}" CFLAGS="${enable_debug_save_CFLAGS}" CXXFLAGS="${enable_debug_save_CXXFLAGS}" LDFLAGS="${enable_debug_save_LDFLAGS}" AC_DEFINE( [CODE_DEBUG], [], [Define to include debugging information] ) is_debug=yes else COVFLAGS="${enable_debug_save_COVFLAGS}" COVLFLAGS="${enable_debug_save_COVLFLAGS}" CFLAGS="${enable_debug_save_CFLAGS}" CXXFLAGS="${enable_debug_save_CXXFLAGS}" LDFLAGS="${enable_debug_save_LDFLAGS}" fi ]) AC_SUBST(COVFLAGS) AC_SUBST(COVLFLAGS) AC_MSG_RESULT($is_debug) ]) dnl for lua (KSW was here) AC_DEFUN([KSW_HAVE_LUA], [ AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING(for lua) AC_ARG_WITH([lua-dir], [ --with-lua-dir=DIR DIR is equal to the install prefix of Lua. Header files are in DIR/include, and Library files are in DIR/lib]) AC_ARG_WITH([lua-include-dir], [ --with-lua-include-dir=DIR Lua header files are in DIR]) AC_ARG_WITH([lua-lib-dir], [ --with-lua-lib-dir=DIR Lua libraries are in DIR]) AC_ARG_WITH([lua-lib], [ --with-lua-lib=LIB Use -lLIB to link with the lua library]) if test x"$with_lua_dir" = x && test x"$with_lua_include_dir" = x && test x"$with_lua_lib_dir" = x && test x"$with_lua_lib" = x; then # user did not request Lua support, disable it have_lua="disabled" else # "yes" is a bogus option if test x"$with_lua_dir" = xyes; then with_lua_dir= fi if test x"$with_lua_include_dir" = xyes; then with_lua_include_dir= fi if test x"$with_lua_lib_dir" = xyes; then with_lua_lib_dir= fi if test x"$with_lua_lib" = xyes; then with_lua_lib= fi # No lua unless we discover otherwise have_lua=no # Check whether we are requested to link with a specific version if test x"$with_lua_lib" != x; then ksw_lua_lib="$with_lua_lib" fi # Check whether we were supplied with an answer already if test x"$with_lua_dir" != x; then have_lua=yes ksw_lua_dir="$with_lua_dir" ksw_lua_include_dir="$with_lua_dir/include" if test -d "$with_lua_dir/lib64" ; then ksw_lua_lib_dir="$with_lua_dir/lib64" else ksw_lua_lib_dir="$with_lua_dir/lib" fi # Only search for the lib if the user did not define one already if test x"$ksw_lua_lib" = x; then ksw_lua_lib="`ls $ksw_lua_lib_dir/liblua5* 2> /dev/null | sed -n 1p | sed s@$ksw_lua_lib_dir/lib@@ | [sed s@[.].*@@]`" fi if test x"$ksw_lua_lib" = x; then ksw_lua_lib="`ls $ksw_lua_lib_dir/liblua.* 2> /dev/null | sed -n 1p | sed s@$ksw_lua_lib_dir/lib@@ | [sed s@[.].*@@]`" fi ksw_lua_LIBS="-L$ksw_lua_lib_dir -l$ksw_lua_lib -lm -ldl" else # Use cached value or do search, starting with suggestions from # the command line AC_CACHE_VAL(ksw_cv_have_lua, [ # We are not given a solution and there is no cached value. ksw_lua_dir= if test x"$ksw_lua_include_dir" = x; then ksw_lua_include_dir="`ls -dr /usr/include/lua.h /usr/local/include/lua.h /usr/include/lua*/lua.h /usr/local/include/lua*/lua.h 2> /dev/null | sed -n 1p | sed s@/lua.h@@`" fi if test x"$ksw_lua_lib" = x; then ksw_lua_lib_dir="`ls -dr /usr/lib64/liblua5* /usr/lib64/liblua.* /usr/local/lib64/liblua5* /usr/local/lib64/liblua.* /usr/lib/liblua5* /usr/lib/liblua.* /usr/local/lib/liblua5* /usr/local/lib/liblua.* 2> /dev/null | sed -n 1p`" ksw_lua_lib="`echo $ksw_lua_lib_dir | sed 's@/.*/@@' `" ksw_lua_lib_dir="`echo $ksw_lua_lib_dir | sed s@/$ksw_lua_lib@@ `" ksw_lua_lib="`echo $ksw_lua_lib | [sed s@[.].*@@] | sed s@^lib@@ `" fi if test x"$ksw_lua_lib" != x; then if test x"$ksw_lua_lib_dir" = x; then ksw_lua_LIBS="-l$ksw_lua_lib -lm -ldl" else ksw_lua_LIBS="-L$ksw_lua_lib_dir -l$ksw_lua_lib -lm -ldl" fi # Record where we found lua for the cache. ksw_cv_have_lua="have_lua=yes \ ksw_lua_dir=$ksw_lua_dir \ ksw_lua_include_dir=$ksw_lua_include_dir \ ksw_lua_LIBS=\"$ksw_lua_LIBS\"" fi ])dnl eval "$ksw_cv_have_lua" fi # all $ksw_lua_* are set fi # $have_lua reflects the system status if test x"$have_lua" = xyes; then LUA_CCFLAGS="-I$ksw_lua_include_dir" LUA_DIR="$ksw_lua_dir" LUA_LIBS="$ksw_lua_LIBS" # All variables are defined, report the result AC_DEFINE( [HAVE_LUA], [], [Define when you have Lua installed] ) AC_MSG_RESULT([$have_lua: LUA_CCFLAGS=$LUA_CCFLAGS LUA_DIR=$LUA_DIR LUA_LIBS=$LUA_LIBS ]) else # lua was not found LUA_CCFLAGS= LUA_DIR= LUA_LIBS= AC_MSG_RESULT($have_lua) fi AC_SUBST(LUA_CCFLAGS) AC_SUBST(LUA_DIR) AC_SUBST(LUA_LIBS) #### Being paranoid: if test x"$have_lua" = xyes; then AC_MSG_CHECKING(correct functioning of lua installation) AC_CACHE_VAL(ksw_cv_lua_test_result, [ cat > ksw_lua_test.h << EOF EOF cat > ksw_lua_test.c << EOF #include "ksw_lua_test.h" #include #include //#include int main( int argc, char **argv ) { lua_State * L = luaL_newstate(); //luaopen_math( L ); lua_close( L ); return( 0 ); } EOF ksw_cv_lua_test_result="failure" ksw_try_1="$CC $LUA_CCFLAGS $LUA_LIBS -o ksw_lua_test ksw_lua_test.c >/dev/null 2>ksw_lua_test_1.out" AC_TRY_EVAL(ksw_try_1) ksw_err_1=`grep -v '^ *+' ksw_lua_test_1.out | grep -v "^ksw_lua_test.{$ac_ext}\$"` if test x"$ksw_err_1" != x; then echo "$ksw_err_1" >&AC_FD_CC echo "configure: could not compile:" >&AC_FD_CC cat ksw_lua_test.c >&AC_FD_CC else ksw_cv_lua_test_result="success" fi ])dnl AC_CACHE_VAL ksw_cv_lua_test_result AC_MSG_RESULT([$ksw_cv_lua_test_result]); if test x"$ksw_cv_lua_test_result" = "xfailure"; then AC_MSG_ERROR([Failed to find matching components of a complete lua installation. Try using more options, see ./configure --help.]) fi rm -f ksw_lua_test.h \ ksw_lua_test.c ksw_lua_test.o ksw_lua_test \ ksw_lua_test_1.out fi ]) dnl for lualib (KSW was here) AC_DEFUN([KSW_HAVE_LUALIB], [ if test x"${LUA_LIBS}" != x; then AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([KSW_HAVE_LUA]) AC_MSG_CHECKING(for lualib) AC_ARG_WITH([lualib-dir], [ --with-lualib-dir=DIR DIR is equal to the install prefix of Lualib. Header files are in DIR/include, and Library files are in DIR/lib]) AC_ARG_WITH([lualib-include-dir], [ --with-lualib-include-dir=DIR Lua header files are in DIR]) AC_ARG_WITH([lualib-lib-dir], [ --with-lualib-lib-dir=DIR Lua libraries are in DIR]) AC_ARG_WITH([lualib-lib], [ --with-lualib-lib=LIB Use -lLIB to link with the lualib library]) if test x"$with_lualib_dir" = x"no" || test x"$with_lualib_include_dir" = x"no" || test x"$with_lualib_lib_dir" = x"no" || test x"$with_lualib_lib" = x"no"; then # user disabled lualib. Leave cache alone. have_lualib="User disabled lualib." else # "yes" is a bogus option if test x"$with_lualib_dir" = xyes; then with_lualib_dir= fi if test x"$with_lualib_include_dir" = xyes; then with_lualib_include_dir= fi if test x"$with_lualib_lib_dir" = xyes; then with_lualib_lib_dir= fi if test x"$with_lualib_lib" = xyes; then with_lualib_lib= fi # No lualib unless we discover otherwise have_lualib=no # Check whether we are requested to link with a specific version if test x"$with_lualib_lib" != x; then ksw_lualib_lib="$with_lualib_lib" fi # Check whether we were supplied with an answer already if test x"$with_lualib_dir" != x; then have_lualib=yes ksw_lualib_dir="$with_lualib_dir" ksw_lualib_include_dir="$with_lualib_dir/include" if test -d "$with_lualib_dir/lib64" ; then ksw_lualib_lib_dir="$with_lualib_dir/lib64" else ksw_lualib_lib_dir="$with_lualib_dir/lib" fi # Only search for the lib if the user did not define one already if test x"$ksw_lualib_lib" = x; then ksw_lualib_lib="`ls $ksw_lualib_lib_dir/liblualib5* 2> /dev/null | sed -n 1p | sed s@$ksw_lualib_lib_dir/lib@@ | [sed s@[.].*@@]`" fi if test x"$ksw_lualib_lib" = x; then ksw_lualib_lib="`ls $ksw_lualib_lib_dir/liblualib.* 2> /dev/null | sed -n 1p | sed s@$ksw_lualib_lib_dir/lib@@ | [sed s@[.].*@@]`" fi ksw_lualib_LIBS="-L$ksw_lualib_lib_dir -l$ksw_lualib_lib" else # Use cached value or do search, starting with suggestions from # the command line AC_CACHE_VAL(ksw_cv_have_lualib, [ # We are not given a solution and there is no cached value. ksw_lualib_dir= if test x"$ksw_lualib_include_dir" = x; then ksw_lualib_include_dir="`ls -dr /usr/include/lualib.h /usr/local/include/lualib.h /usr/include/lua*/lualib.h /usr/local/include/lua*/lualib.h 2> /dev/null | sed -n 1p | sed s@/lualib.h@@`" fi if test x"$ksw_lualib_lib" = x; then ksw_lualib_lib_dir="`ls -dr /usr/lib64/liblualib5* /usr/lib64/liblualib.* /usr/local/lib64/liblualib5* /usr/local/lib64/liblualib.* /usr/lib/liblualib5* /usr/lib/liblualib.* /usr/local/lib/liblualib5* /usr/local/lib/liblualib.* 2> /dev/null | sed -n 1p`" ksw_lualib_lib="`echo $ksw_lualib_lib_dir | sed 's@/.*/@@' `" ksw_lualib_lib_dir="`echo $ksw_lualib_lib_dir | sed s@/$ksw_lualib_lib@@ `" ksw_lualib_lib="`echo $ksw_lualib_lib | [sed s@[.].*@@] | sed s@^lib@@ `" fi if test x"$ksw_lualib_lib" != x; then if test x"$ksw_lualib_lib_dir" = x; then ksw_lualib_LIBS="-l$ksw_lualib_lib" else ksw_lualib_LIBS="-L$ksw_lualib_lib_dir -l$ksw_lualib_lib" fi # Record where we found lualib for the cache. ksw_cv_have_lualib="have_lualib=yes \ ksw_lualib_dir=$ksw_lualib_dir \ ksw_lualib_include_dir=$ksw_lualib_include_dir \ ksw_lualib_LIBS=\"$ksw_lualib_LIBS\"" fi ])dnl eval "$ksw_cv_have_lualib" fi # all $ksw_lualib_* are set fi # $have_lualib reflects the system status if test x"$have_lualib" = xyes; then LUALIB_CCFLAGS="-I$ksw_lualib_include_dir" LUALIB_DIR="$ksw_lualib_dir" LUALIB_LIBS="-lm -ldl $LUA_LIBS $ksw_lualib_LIBS" # All variables are defined, report the result AC_DEFINE( [HAVE_LUALIB], [], [Define when you have Lua libs installed] ) AC_MSG_RESULT([$have_lualib: LUALIB_CCFLAGS=$LUALIB_CCFLAGS LUALIB_DIR=$LUALIB_DIR LUALIB_LIBS=$LUALIB_LIBS ]) else # lualib was not found LUALIB_CCFLAGS= LUALIB_DIR= LUALIB_LIBS= AC_MSG_RESULT($have_lualib) fi AC_SUBST(LUALIB_CCFLAGS) AC_SUBST(LUALIB_DIR) AC_SUBST(LUALIB_LIBS) #### Being paranoid: if test x"$have_lualib" = xyes; then AC_MSG_CHECKING(correct functioning of lualib installation) AC_CACHE_VAL(ksw_cv_lualib_test_result, [ cat > ksw_lualib_test.h << EOF EOF cat > ksw_lualib_test.c << EOF #include "ksw_lualib_test.h" #include #include #include int main( int argc, char **argv ) { lua_State * L = luaL_newstate(); luaopen_math( L ); lua_close( L ); return( 0 ); } EOF ksw_cv_lualib_test_result="failure" ksw_try_1="$CC $LUALIB_CCFLAGS $LUALIB_LIBS -o ksw_lualib_test ksw_lualib_test.c >/dev/null 2>ksw_lualib_test_1.out" AC_TRY_EVAL(ksw_try_1) ksw_err_1=`grep -v '^ *+' ksw_lualib_test_1.out | grep -v "^ksw_lualib_test.{$ac_ext}\$"` if test x"$ksw_err_1" != x; then echo "$ksw_err_1" >&AC_FD_CC echo "configure: could not compile:" >&AC_FD_CC cat ksw_lualib_test.c >&AC_FD_CC else ksw_cv_lualib_test_result="success" fi ])dnl AC_CACHE_VAL ksw_cv_lualib_test_result AC_MSG_RESULT([$ksw_cv_lualib_test_result]); if test x"$ksw_cv_lualib_test_result" = "xfailure"; then AC_MSG_ERROR([Failed to find matching components of a complete lualib installation. Try using more options, see ./configure --help.]) fi rm -f ksw_lualib_test.h \ ksw_lualib_test.c ksw_lualib_test.o ksw_lualib_test \ ksw_lualib_test_1.out fi fi ]) dnl for Qt (KSW was here) AC_DEFUN([BNV_HAVE_QT], [ dnl THANKS! This code includes bug fixes by: dnl Tim McClarren. AC_REQUIRE([AC_PROG_CXX]) AC_LANG_SAVE AC_LANG_CPLUSPLUS AC_MSG_CHECKING(for Qt) AC_ARG_WITH([Qt-dir], [ --with-Qt-dir=DIR DIR is equal to \$QTDIR if you have followed the installation instructions of Trolltech. Header files are in DIR/include, binary utilities are in DIR/bin, the library is in DIR/lib, and the translations are in DIR/translations]) AC_ARG_WITH([Qt-include-dir], [ --with-Qt-include-dir=DIR Qt header files are in DIR]) AC_ARG_WITH([Qt-bin-dir], [ --with-Qt-bin-dir=DIR Qt utilities such as moc and uic are in DIR]) AC_ARG_WITH([Qt-lib-dir], [ --with-Qt-lib-dir=DIR The Qt library is in DIR]) AC_ARG_WITH([Qt-translations-dir], [ --with-Qt-translations-dir=DIR The Qt translations are in DIR]) bnv_is_qt5=no if test x"$is_osx" = xyes; then bnv_qt5_libs="-framework QtCore -framework QtGui -framework QtWidgets" else bnv_qt5_libs="-lQt5Core -lQt5Gui -lQt5Widgets" fi if test x"$with_Qt_dir" = x"no" || test x"$with_Qt_include_dir" = x"no" || test x"$with_Qt_bin_dir" = x"no" || test x"$with_Qt_lib_dir" = x"no" || test x"$with_Qt_translations_dir" = x"no"; then # user disabled Qt. Leave cache alone. have_qt="User disabled Qt." else # "yes" is a bogus option if test x"$with_Qt_dir" = xyes; then with_Qt_dir= fi if test x"$with_Qt_include_dir" = xyes; then with_Qt_include_dir= fi if test x"$with_Qt_bin_dir" = xyes; then with_Qt_bin_dir= fi if test x"$with_Qt_lib_dir" = xyes; then with_Qt_lib_dir= fi if test x"$with_Qt_translations_dir" = xyes; then with_Qt_translations_dir= fi # No Qt unless we discover otherwise have_qt=no # Check whether we were supplied with an answer already if test x"$with_Qt_dir" != x; then have_qt=yes bnv_qt_dir="$with_Qt_dir" bnv_qt_include_dir="$with_Qt_dir/include" bnv_qt_bin_dir="$with_Qt_dir/bin" if test -d "$with_Qt_dir/lib64" ; then bnv_qt_lib_dir="$with_Qt_dir/lib64" else bnv_qt_lib_dir="$with_Qt_dir/lib" fi bnv_qt_translations_dir="$with_Qt_dir/translations" if test x"$is_osx" = xyes; then bnv_qt_LIBS="-F$bnv_qt_dir/lib -L$bnv_qt_lib_dir $bnv_qt5_libs " else bnv_qt_LIBS="-L$bnv_qt_lib_dir $bnv_qt5_libs " fi else # Use cached value or do search, starting with suggestions from # the command line AC_CACHE_VAL(bnv_cv_have_qt, [ # We are not given a solution and there is no cached value. bnv_qt_dir=NO bnv_qt_include_dir=NO bnv_qt_lib_dir=NO bnv_qt_translations_dir= BNV_PATH_QT_DIRECT if test "$bnv_qt_dir" = NO || test "$bnv_qt_include_dir" = NO || test "$bnv_qt_lib_dir" = NO; then # Problem with finding complete Qt. Cache the known absence of Qt. bnv_cv_have_qt="have_qt=no" else # Record where we found Qt for the cache. bnv_cv_have_qt="have_qt=yes \ bnv_qt_dir=$bnv_qt_dir \ bnv_qt_include_dir=$bnv_qt_include_dir \ bnv_qt_bin_dir=$bnv_qt_bin_dir \ bnv_qt_translations_dir=$bnv_qt_translations_dir \ bnv_is_qt5=$bnv_is_qt5 \ bnv_qt_LIBS=\"$bnv_qt_LIBS\"" fi ])dnl eval "$bnv_cv_have_qt" fi # all $bnv_qt_* are set fi # $have_qt reflects the system status if test x"$have_qt" = xyes; then if test x"$is_osx" = xyes; then QT_CXXFLAGS="-I$bnv_qt_include_dir -F$bnv_qt_dir/lib" else QT_CXXFLAGS="-I$bnv_qt_include_dir" fi QT_DIR="$bnv_qt_dir" QT_TRANSLATIONS_DIR="$bnv_qt_translations_dir" QT_LIBS="$bnv_qt_LIBS" if test x"$bnv_qt_bin_dir" != x; then # We were told where to look for the utilities? # UIC detection if test -x "$bnv_qt_bin_dir/uic"; then QT_UIC="$bnv_qt_bin_dir/uic" fi # MOC detection if test -x "$bnv_qt_bin_dir/moc"; then QT_MOC="$bnv_qt_bin_dir/moc" fi # RCC detection if test -x "$bnv_qt_bin_dir/rcc"; then QT_RCC="$bnv_qt_bin_dir/rcc" fi # LRELEASE detection if test -x "$bnv_qt_bin_dir/lrelease"; then QT_LRELEASE="$bnv_qt_bin_dir/lrelease" fi # LCONVERT detection if test -x "$bnv_qt_bin_dir/lconvert"; then QT_LCONVERT="$bnv_qt_bin_dir/lconvert" fi # MACDEPLOYQT detection if test -x "$bnv_qt_bin_dir/macdeployqt"; then QT_MACDEPLOYQT="$bnv_qt_bin_dir/macdeployqt" fi elif test x"$bnv_qt_dir" != x; then # If bnv_qt_dir is defined, utilities are expected to be in the # bin subdirectory # UIC detection if test -x "$bnv_qt_dir/bin/uic"; then QT_UIC="$bnv_qt_dir/bin/uic" fi # MOC detection if test -x "$bnv_qt_dir/bin/moc"; then QT_MOC="$bnv_qt_dir/bin/moc" fi # RCC detection if test -x "$bnv_qt_dir/bin/rcc"; then QT_RCC="$bnv_qt_dir/bin/rcc" fi # LRELEASE detection if test -x "$bnv_qt_dir/bin/lrelease"; then QT_LRELEASE="$bnv_qt_dir/bin/lrelease" fi # LCONVERT detection if test -x "$bnv_qt_dir/bin/lconvert"; then QT_LCONVERT="$bnv_qt_dir/bin/lconvert" fi # MACDEPLOYQT detection if test -x "$bnv_qt_dir/bin/macdeployqt"; then QT_MACDEPLOYQT="$bnv_qt_dir/bin/macdeployqt" fi fi # If binaries are still not set, try /usr/lib/x86_64-linux-gnu/qt5/bin/ if test x"$host_alias" != x; then # set by configure --host bnv_qt_lib_host=$host_alias else bnv_qt_lib_host=`$SHELL "$srcdir/config.guess" | cut -d'-' -f 1,3-4` fi if test x"$QT_UIC" = x; then # UIC detection if test -x "/usr/lib/$bnv_qt_lib_host/qt5/bin/uic"; then QT_UIC="/usr/lib/$bnv_qt_lib_host/qt5/bin/uic" fi fi if test x"$QT_MOC" = x; then # MOC detection if test -x "/usr/lib/$bnv_qt_lib_host/qt5/bin/moc"; then QT_MOC="/usr/lib/$bnv_qt_lib_host/qt5/bin/moc" fi fi if test x"$QT_RCC" = x; then # RCC detection if test -x "/usr/lib/$bnv_qt_lib_host/qt5/bin/rcc"; then QT_RCC="/usr/lib/$bnv_qt_lib_host/qt5/bin/rcc" fi fi if test x"$QT_LRELEASE" = x; then # LRELEASE detection if test -x "/usr/lib/$bnv_qt_lib_host/qt5/bin/lrelease"; then QT_LRELEASE="/usr/lib/$bnv_qt_lib_host/qt5/bin/lrelease" fi fi if test x"$QT_LCONVERT" = x; then # LCONVERT detection if test -x "/usr/lib/$bnv_qt_lib_host/qt5/bin/lconvert"; then QT_LCONVERT="/usr/lib/$bnv_qt_lib_host/qt5/bin/lconvert" fi fi if test x"$QT_MACDEPLOYQT" = x; then # MACDEPLOYQT detection if test -x "/usr/lib/$bnv_qt_lib_host/qt5/bin/macdeployqt"; then QT_MACDEPLOYQT="/usr/lib/$bnv_qt_lib_host/qt5/bin/macdeployqt" fi fi # If binaries are still not set, try -qt5 suffix (Fedora) if test x"$QT_UIC" = x; then # UIC detection if test `which uic-qt5 2> /dev/null`; then QT_UIC="uic-qt5" fi fi if test x"$QT_MOC" = x; then # MOC detection if test `which moc-qt5 2> /dev/null`; then QT_MOC="moc-qt5" fi fi if test x"$QT_RCC" = x; then # RCC detection if test `which rcc-qt5 2> /dev/null`; then QT_RCC="rcc-qt5" fi fi if test x"$QT_LRELEASE" = x; then # LRELEASE detection if test `which lrelease-qt5 2> /dev/null`; then QT_LRELEASE="lrelease-qt5" fi fi if test x"$QT_LCONVERT" = x; then # LCONVERT detection if test `which lconvert-qt5 2> /dev/null`; then QT_LCONVERT="lconvert-qt5" fi fi if test x"$QT_MACDEPLOYQT" = x; then # MACDEPLOYQT detection if test `which macdeployqt-qt5 2> /dev/null`; then QT_MACDEPLOYQT="macdeployqt-qt5" fi fi # If binaries are still not set, try qtchooser qtchoosertoolsdir= if test `which qtchooser 2> /dev/null`; then # Example of "qtchooser -qt=5 -print-env" output: # QT_SELECT="5" # QTTOOLDIR="/usr/lib/qt5/bin" # QTLIBDIR="/usr/lib/x86_64-linux-gnu" qtchoosertoolsdir=$(qtchooser -qt=5 -print-env | grep QTTOOLDIR | cut -d\" -f2) fi if test x"$qtchoosertoolsdir" != x; then if test x"$QT_UIC" = x; then # UIC detection if test -x "$qtchoosertoolsdir/uic"; then QT_UIC="$qtchoosertoolsdir/uic" fi fi if test x"$QT_MOC" = x; then # MOC detection if test -x "$qtchoosertoolsdir/moc"; then QT_MOC="$qtchoosertoolsdir/moc" fi fi if test x"$QT_RCC" = x; then # RCC detection if test -x "$qtchoosertoolsdir/rcc"; then QT_RCC="$qtchoosertoolsdir/rcc" fi fi if test x"$QT_LRELEASE" = x; then # LRELEASE detection if test -x "$qtchoosertoolsdir/lrelease"; then QT_LRELEASE="$qtchoosertoolsdir/lrelease" fi fi if test x"$QT_LCONVERT" = x; then # LCONVERT detection if test -x "$qtchoosertoolsdir/lconvert"; then QT_LCONVERT="$qtchoosertoolsdir/lconvert" fi fi if test x"$QT_MACDEPLOYQT" = x; then # MACDEPLOYQT detection if test -x "$qtchoosertoolsdir/macdeployqt"; then QT_MACDEPLOYQT="$qtchoosertoolsdir/macdeployqt" fi fi fi # If binaries are still not set, try $PATH if test x"$QT_UIC" = x; then # UIC detection if test `which uic 2> /dev/null`; then QT_UIC=`which uic` fi fi if test x"$QT_MOC" = x; then # MOC detection if test `which moc 2> /dev/null`; then QT_MOC=`which moc` fi fi if test x"$QT_RCC" = x; then # RCC detection if test `which rcc 2> /dev/null`; then QT_RCC=`which rcc` fi fi if test x"$QT_LRELEASE" = x; then # LRELEASE detection if test `which lrelease 2> /dev/null`; then QT_LRELEASE=`which lrelease` fi fi if test x"$QT_LCONVERT" = x; then # LCONVERT detection if test `which lconvert 2> /dev/null`; then QT_LCONVERT=`which lconvert` fi fi if test x"$QT_MACDEPLOYQT" = x; then # MACDEPLOYQT detection if test `which macdeployqt 2> /dev/null`; then QT_MACDEPLOYQT=`which macdeployqt` fi fi # If binaries are still not set, fail QT_MISSING_TOOLS= if test x"$QT_UIC" = x; then have_qt=no QT_MISSING_TOOLS="$QT_MISSING_TOOLS uic" fi if test x"$QT_MOC" = x; then have_qt=no QT_MISSING_TOOLS="$QT_MISSING_TOOLS moc" fi if test x"$QT_RCC" = x; then have_qt=no QT_MISSING_TOOLS="$QT_MISSING_TOOLS rcc" fi if test x"$QT_LRELEASE" = x; then have_qt=no QT_MISSING_TOOLS="$QT_MISSING_TOOLS lrelease" fi if test x"$QT_LCONVERT" = x; then have_qt=no QT_MISSING_TOOLS="$QT_MISSING_TOOLS lconvert" fi # Allow macdeployqt to be absent if test x"$QT_MACDEPLOYQT" = x; then QT_MACDEPLOYQT="macdeployqt" fi # All variables are defined, report the result AC_MSG_RESULT([$have_qt: QT_CXXFLAGS=$QT_CXXFLAGS QT_DIR=$QT_DIR QT_TRANSLATIONS_DIR=$QT_TRANSLATIONS_DIR QT_LIBS=$QT_LIBS QT_UIC=$QT_UIC QT_MOC=$QT_MOC QT_RCC=$QT_RCC QT_LRELEASE=$QT_LRELEASE QT_LCONVERT=$QT_LCONVERT QT_MACDEPLOYQT=$QT_MACDEPLOYQT]) if test x"$QT_MISSING_TOOLS" != x; then AC_MSG_ERROR([Failed to find Qt tool programs:$QT_MISSING_TOOLS. Try using more options, see ./configure --help.]) fi else # Qt was not found QT_CXXFLAGS= QT_DIR= QT_TRANSLATIONS_DIR= QT_LIBS= QT_UIC= QT_MOC= QT_RCC= QT_LRELEASE= QT_LCONVERT= QT_MACDEPLOYQT= AC_MSG_RESULT($have_qt) fi if test x"$bnv_is_qt5" = xyes; then HAVE_QT5=1 AC_SUBST(HAVE_QT5) AC_DEFINE( [HAVE_QT5], [], [Define when you have QT5 installed] ) fi AC_SUBST(QT_CXXFLAGS) AC_SUBST(QT_DIR) AC_SUBST(QT_TRANSLATIONS_DIR) AC_SUBST(QT_LIBS) AC_SUBST(QT_UIC) AC_SUBST(QT_MOC) AC_SUBST(QT_RCC) AC_SUBST(QT_LRELEASE) AC_SUBST(QT_LCONVERT) AC_SUBST(QT_MACDEPLOYQT) #### Being paranoid: if test x"$have_qt" = xyes; then AC_MSG_CHECKING(correct functioning of Qt installation) AC_CACHE_VAL(bnv_cv_qt_test_result, [ cat > bnv_qt_test.h << EOF #include class Test : public QObject { Q_OBJECT public: Test() {} ~Test() {} public slots: void receive() {} signals: void send(); }; EOF cat > bnv_qt_main.$ac_ext << EOF #include "bnv_qt_test.h" #include int main( int argc, char **argv ) { QApplication app( argc, argv ); Test t; QObject::connect( &t, SIGNAL(send()), &t, SLOT(receive()) ); } EOF # Some versions of Clang or use of separate include directory causes # a warning for unused macOS framework with no way to disable it. ignore_warning="clang: warning: -framework .*: 'linker' input unused" bnv_cv_qt_test_result="failure" bnv_try_1="$QT_MOC bnv_qt_test.h -o moc_bnv_qt_test.$ac_ext >/dev/null 2>bnv_qt_test_1.out" AC_TRY_EVAL(bnv_try_1) bnv_err_1=`grep -v '^ *+' bnv_qt_test_1.out | grep -v "^bnv_qt_test.h\$"` if test x"$bnv_err_1" != x; then echo "$bnv_err_1" >&AC_FD_CC echo "configure: could not run $QT_MOC on:" >&AC_FD_CC cat bnv_qt_test.h >&AC_FD_CC else bnv_try_2="$CXX $QT_CXXFLAGS -c $CXXFLAGS -Wno-non-virtual-dtor -o moc_bnv_qt_test.o moc_bnv_qt_test.$ac_ext >/dev/null 2>bnv_qt_test_2.out" AC_TRY_EVAL(bnv_try_2) bnv_err_2=`grep -v '^ *+' bnv_qt_test_2.out | grep -v "^moc_bnv_qt_test.${ac_ext}\$" | grep -v "^${ignore_warning}\$"` if test x"$bnv_err_2" != x; then echo "$bnv_err_2" >&AC_FD_CC echo "configure: could not compile:" >&AC_FD_CC cat moc_bnv_qt_test.$ac_ext >&AC_FD_CC else bnv_try_3="$CXX $QT_CXXFLAGS -c $CXXFLAGS -o bnv_qt_main.o bnv_qt_main.$ac_ext >/dev/null 2>bnv_qt_test_3.out" AC_TRY_EVAL(bnv_try_3) bnv_err_3=`grep -v '^ *+' bnv_qt_test_3.out | grep -v "^bnv_qt_main.${ac_ext}\$" | grep -v "^${ignore_warning}\$"` if test x"$bnv_err_3" != x; then echo "$bnv_err_3" >&AC_FD_CC echo "configure: could not compile:" >&AC_FD_CC cat bnv_qt_main.$ac_ext >&AC_FD_CC else bnv_try_4="$CXX $LDFLAGS -o bnv_qt_main bnv_qt_main.o moc_bnv_qt_test.o $QT_LIBS $LIBS >/dev/null 2>bnv_qt_test_4.out" AC_TRY_EVAL(bnv_try_4) bnv_err_4=`grep -v '^ *+' bnv_qt_test_4.out` if test x"$bnv_err_4" != x; then echo "$bnv_err_4" >&AC_FD_CC else bnv_cv_qt_test_result="success" fi fi fi fi ])dnl AC_CACHE_VAL bnv_cv_qt_test_result AC_MSG_RESULT([$bnv_cv_qt_test_result]); if test x"$bnv_cv_qt_test_result" = "xfailure"; then AC_MSG_ERROR([Failed to find matching components of a complete Qt installation. Try using more options, see ./configure --help.]) fi rm -f bnv_qt_test.h moc_bnv_qt_test.$ac_ext moc_bnv_qt_test.o \ bnv_qt_main.$ac_ext bnv_qt_main.o bnv_qt_main \ bnv_qt_test_1.out bnv_qt_test_2.out bnv_qt_test_3.out bnv_qt_test_4.out fi AC_LANG_RESTORE ]) dnl Internal subroutine of BNV_HAVE_QT dnl Set bnv_qt_dir bnv_qt_include_dir bnv_qt_bin_dir bnv_qt_lib_dir dnl Copyright 2001 Bastiaan N. Veelo dnl Modified in 2018 by Zack Middleton AC_DEFUN([BNV_PATH_QT_DIRECT], [ if test x"$host_alias" != x; then # set by configure --host bnv_qt_host=$host_alias else bnv_qt_host=`$SHELL "$srcdir/config.guess" | cut -d'-' -f 1,3-4` fi ## Binary utilities ## if test x"$with_Qt_bin_dir" != x; then bnv_qt_bin_dir=$with_Qt_bin_dir fi ## Look for header files ## if test x"$with_Qt_include_dir" != x; then bnv_qt_include_dir="$with_Qt_include_dir" else # Before Qt 5.7.0 use QT_VERSION define in qglobal.h # Qt 5.7.0 and later use QT_VERSION_* defines in qconfig.h (Debian) or qconfig-32.h and qconfig-64.h (Fedora) # Look for the header files in a standard set of common directories. bnv_include_path_list=" /usr/include/$bnv_qt_host/qt5 /usr/include/qt5 /usr/include/qt /usr/qt5/include /usr/include /usr/local/include/$bnv_qt_host/qt5 /usr/local/include/qt5 /usr/local/qt5/include /usr/local/include `ls -dr /usr/local/Cellar/qt/5*/include 2>/dev/null` " # Now look for the newest in this list bnv_prev_ver=0x04FFFF for bnv_dir in $bnv_include_path_list; do # Qt 5.7.0 and later ztm_qconfig_list=" $bnv_dir/QtCore/qconfig.h $bnv_dir/QtCore/qconfig-32.h $bnv_dir/QtCore/qconfig-64.h " for ztm_qconfig in $ztm_qconfig_list; do if test -r "$ztm_qconfig"; then ztm_qt_ver_major=`egrep -w '^#define[[:space:]]*QT_VERSION_MAJOR[[:space:]]*' "$ztm_qconfig" | sed s/'^#define[[:space:]]*QT_VERSION_MAJOR[[:space:]]*'//` ztm_qt_ver_minor=`egrep -w '^#define[[:space:]]*QT_VERSION_MINOR[[:space:]]*' "$ztm_qconfig" | sed s/'^#define[[:space:]]*QT_VERSION_MINOR[[:space:]]*'//` ztm_qt_ver_patch=`egrep -w '^#define[[:space:]]*QT_VERSION_PATCH[[:space:]]*' "$ztm_qconfig" | sed s/'^#define[[:space:]]*QT_VERSION_PATCH[[:space:]]*'//` if test x"$ztm_qt_ver_major" != x -a x"$ztm_qt_ver_minor" != x -a x"$ztm_qt_ver_patch" != x; then bnv_this_ver=`printf "0x%02X%02X%02X" $ztm_qt_ver_major $ztm_qt_ver_minor $ztm_qt_ver_patch` if expr $bnv_this_ver '>' $bnv_prev_ver > /dev/null; then bnv_is_qt5=yes bnv_qt_include_dir=$bnv_dir bnv_prev_ver=$bnv_this_ver fi break fi fi done # Before Qt 5.7.0 the version was a constant (i.e., 0x050600 for 5.6.0) in qglobal.h if test -r "$bnv_dir/QtCore/qglobal.h"; then bnv_this_ver=`egrep -w '^#define[[:space:]]*QT_VERSION[[:space:]]*' $bnv_dir/QtCore/qglobal.h | sed s/'^#define[[:space:]]*QT_VERSION[[:space:]]*'//` if case "$bnv_this_ver" in 0x* ) true;; *) false;; esac; then if expr $bnv_this_ver '>' $bnv_prev_ver > /dev/null; then bnv_is_qt5=yes bnv_qt_include_dir=$bnv_dir bnv_prev_ver=$bnv_this_ver fi fi fi done fi dnl Found header files. # Are these headers located in a traditional Trolltech installation? # That would be $bnv_qt_include_dir stripped from its last element: bnv_found_traditional=no bnv_possible_qt_dir=`dirname $bnv_qt_include_dir` if test -x $bnv_possible_qt_dir/bin/moc; then if ls $bnv_possible_qt_dir/lib/libQt5Core.* 1> /dev/null 2> /dev/null; then bnv_found_traditional=yes elif test -r $bnv_possible_qt_dir/lib/QtCore.framework; then bnv_found_traditional=yes fi fi if test x"$bnv_found_traditional" = xyes; then # Then the rest is a piece of cake bnv_qt_dir=$bnv_possible_qt_dir bnv_qt_bin_dir="$bnv_qt_dir/bin" if test -d "$bnv_qt_dir/lib64" ; then bnv_qt_lib_dir="$bnv_qt_dir/lib64" else bnv_qt_lib_dir="$bnv_qt_dir/lib" fi bnv_qt_translations_dir="$bnv_qt_dir/translations" if test x"$is_osx" = xyes; then bnv_qt_LIBS="-F$bnv_qt_dir/lib -L$bnv_qt_lib_dir $bnv_qt5_libs" else bnv_qt_LIBS="-L$bnv_qt_lib_dir $bnv_qt5_libs" fi fi if test $bnv_found_traditional = no; then # There is no valid definition for $QTDIR as Trolltech likes to see it bnv_qt_dir= ## Look for Qt library ## if test x"$with_Qt_lib_dir" != x; then bnv_qt_lib_dir="$with_Qt_lib_dir" bnv_qt_LIBS="-L$bnv_qt_lib_dir $bnv_qt5_libs" else # Normally, when there is no traditional Trolltech installation, # the library is installed in a place where the linker finds it # automatically. qt_direct_test_header=QtWidgets/QApplication qt_direct_test_main=" int argc; char ** argv; QApplication app(argc,argv); " # See if we find the library without any special options. # Don't add top $LIBS permanently yet bnv_save_LIBS="$LIBS" bnv_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-I$bnv_qt_include_dir -fPIC" LIBS="$bnv_qt5_libs" bnv_qt_LIBS="$LIBS" AC_TRY_LINK([#include <$qt_direct_test_header>], $qt_direct_test_main, [ # Succes. # We can link with no special library directory. bnv_qt_lib_dir= ], [ # That did not work. Maybe a library version I don't know about? # Look for some Qt lib in a standard set of common directories. bnv_dir_list=" `echo $bnv_qt_include_dir | sed "s|/include|/lib|"` /usr/lib/$bnv_qt_host /usr/lib/qt5 /usr/qt5/lib /usr/lib64 /usr/lib /usr/local/lib/$bnv_qt_host /usr/local/lib/qt5 /usr/local/qt5/lib /usr/local/lib64 /usr/local/lib /lib64 /lib /opt/lib64 /opt/lib " for bnv_dir in $bnv_dir_list; do if ls $bnv_dir/libQt5Core* > /dev/null 2> /dev/null ; then bnv_qt_lib_dir="$bnv_dir" break fi done # Try with that one LIBS="$bnv_qt5_libs" ]) if test x"$bnv_qt_lib_dir" != x; then bnv_qt_LIBS="-L$bnv_qt_lib_dir $LIBS" else bnv_qt_LIBS="$LIBS" fi LIBS="$bnv_save_LIBS" CXXFLAGS="$bnv_save_CXXFLAGS" fi dnl $with_Qt_lib_dir was not given ## Look for Qt translations ## if test x"$with_Qt_translations_dir" != x; then bnv_qt_translations_dir="$with_Qt_translations_dir" else # Look for Qt translations in a standard set of common directories. bnv_dir_list=" `echo $bnv_qt_include_dir | sed "s|/include|/translations|"` /usr/share/qt5/translations /usr/local/share/qt5/translations " for bnv_dir in $bnv_dir_list; do if ls $bnv_dir/qt_*.qm > /dev/null 2> /dev/null ; then bnv_qt_translations_dir="$bnv_dir" break fi done fi dnl $with_Qt_translations_dir was not given fi dnl Done setting up for non-traditional Trolltech installation ]) AC_DEFUN([MDL_HAVE_OPENGL], [ AC_REQUIRE([AC_PROG_CC]) AC_CACHE_CHECK([for OpenGL], mdl_cv_have_OpenGL, [ dnl Check for Mesa first, unless we were asked not to. AC_ARG_WITH([Mesa], [ --with-Mesa Prefer the Mesa library over a vendors native OpenGL library (default=yes)], with_Mesa_help_string) AC_ARG_ENABLE(Mesa, $with_Mesa_help_string, use_Mesa=$enableval, use_Mesa=yes) AC_ARG_WITH([GL-cflags], [ --with-GL-cflags CFLAGS for linking with GL/GLU libraries]) if test x"$use_Mesa" = xyes; then GL_search_list="MesaGL GL" GLU_search_list="MesaGLU GLU" else GL_search_list="GL MesaGL" GLU_search_list="GLU MesaGLU" fi AC_LANG_SAVE AC_LANG_C if test x"$is_osx" = xyes; then GL_CFLAGS="$with_GL_cflags" GL_LIBS="-framework OpenGL -framework AGL" else GL_CFLAGS="$with_GL_cflags" GL_LIBS="" fi GL_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$GL_CFLAGS" GL_save_LIBS="$LIBS" LIBS="$GL_LIBS" # Save the "AC_MSG_RESULT file descriptor" to FD 8. exec 8>&AC_FD_MSG # Temporarily turn off AC_MSG_RESULT so that the user gets pretty # messages. exec AC_FD_MSG>/dev/null AC_SEARCH_LIBS(glAccum, $GL_search_list, have_GL=yes, have_GL=no) AC_SEARCH_LIBS(gluBeginCurve, $GLU_search_list, have_GLU=yes, have_GLU=no) # Restore pretty messages. exec AC_FD_MSG>&8 if test x"$have_GL" = "xyes" -a x"$have_GLU" = "xyes"; then mdl_cv_have_OpenGL=yes GL_LIBS="$LIBS" AC_SUBST(GL_CFLAGS) AC_SUBST(GL_LIBS) else mdl_cv_have_OpenGL=no GL_LIBS= GL_CFLAGS= fi LIBS="$GL_save_LIBS" CPPFLAGS="$GL_save_CPPFLAGS" AC_LANG_RESTORE dnl bugfix: dont forget to cache this variables, too mdl_cv_GL_CFLAGS="$GL_CFLAGS" mdl_cv_GL_LIBS="$GL_LIBS" mdl_cv_have_GL="$have_GL" mdl_cv_have_GLU="$have_GLU" ]) GL_CFLAGS="$mdl_cv_GL_CFLAGS" GL_LIBS="$mdl_cv_GL_LIBS" have_GL="$mdl_cv_have_GL" have_GLU="$mdl_cv_have_GLU" ]) dnl endof bugfix -ainan AC_DEFUN([KSW_HAVE_DLOPEN], [ AC_REQUIRE([AC_PROG_CC]) AC_CACHE_CHECK([for dlopen], ksw_cv_have_dlopen, [ DLOPEN_save_LIBS="$LIBS" DLOPEN_LIBS="-ldl -rdynamic" LIBS="$DLOPEN_save_LIBS $DLOPEN_LIBS" cat > ksw_dlopen_test.c << EOF #include #include int main( int argc, char ** argv ) { dlopen( "filename", 0 ); return 0; } EOF if ${CC} -o ksw_dlopen_test ksw_dlopen_test.c $LIBS 2> /dev/null > /dev/null; then have_dlopen=yes else DLOPEN_LIBS="-ldl -Wl,-export-dynamic" LIBS="$DLOPEN_save_LIBS $DLOPEN_LIBS" if ${CC} -o ksw_dlopen_test ksw_dlopen_test.c $LIBS 2> /dev/null > /dev/null; then have_dlopen=yes else DLOPEN_LIBS="-ldl" LIBS="$DLOPEN_save_LIBS $DLOPEN_LIBS" if ${CC} -o ksw_dlopen_test ksw_dlopen_test.c $LIBS 2> /dev/null > /dev/null; then have_dlopen=yes else have_dlopen=no DLOPEN_LIBS= fi fi fi rm -f ksw_dlopen_test ksw_dlopen_test.c LIBS="$DLOPEN_save_LIBS" ksw_cv_DLOPEN_LIBS="$DLOPEN_LIBS" ksw_cv_have_dlopen="$have_dlopen" ]) DLOPEN_LIBS="$ksw_cv_DLOPEN_LIBS" have_dlopen="$ksw_cv_have_dlopen" AC_SUBST(DLOPEN_LIBS) if test x"$have_dlopen" = "xyes"; then AC_DEFINE( [HAVE_DLOPEN], [], [Define when you have dlopen function] ) fi ]) AC_DEFUN([VL_PROG_CC_WARNINGS], [ ansi=$1 if test -z "$ansi"; then msg="for C compiler warning flags" else msg="for C compiler warning and ANSI conformance flags" fi AC_CACHE_CHECK($msg, vl_cv_prog_cc_warnings, [ if test -n "$CC"; then cat > conftest.c <&1 | grep -i "WorkShop" > /dev/null 2>&1 && $CC -c -v -Xc conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="-v" else vl_cv_prog_cc_warnings="-v -Xc" fi dnl Digital Unix C compiler elif $CC -V 2>&1 | grep -i "Digital UNIX Compiler" > /dev/null 2>&1 && $CC -c -verbose -w0 -warnprotos -std1 conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="-verbose -w0 -warnprotos" else vl_cv_prog_cc_warnings="-verbose -w0 -warnprotos -std1" fi dnl C for AIX Compiler elif $CC 2>&1 | grep -i "C for AIX Compiler" > /dev/null 2>&1 && $CC -c -qlanglvl=ansi -qinfo=all conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="-qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" else vl_cv_prog_cc_warnings="-qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd -qlanglvl=ansi" fi dnl IRIX C compiler elif $CC -version 2>&1 | grep -i "MIPSpro Compilers" > /dev/null 2>&1 && $CC -c -fullwarn -ansi -ansiE conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="-fullwarn" else vl_cv_prog_cc_warnings="-fullwarn -ansi -ansiE" fi dnl HP-UX C compiler elif what $CC 2>&1 | grep -i "HP C Compiler" > /dev/null 2>&1 && $CC -c -Aa +w1 conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="+w1" else vl_cv_prog_cc_warnings="+w1 -Aa" fi dnl The NEC SX-5 (Super-UX 10) C compiler elif $CC -V 2>&1 | grep "/SX" > /dev/null 2>&1 && $CC -c -pvctl[,]fullmsg -Xc conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="-pvctl[,]fullmsg" else vl_cv_prog_cc_warnings="-pvctl[,]fullmsg -Xc" fi dnl The Cray C compiler (Unicos) elif $CC -V 2>&1 | grep -i "Cray" > /dev/null 2>&1 && $CC -c -h msglevel 2 conftest.c > /dev/null 2>&1 && test -f conftest.o; then if test -z "$ansi"; then vl_cv_prog_cc_warnings="-h msglevel 2" else vl_cv_prog_cc_warnings="-h msglevel 2 -h conform" fi fi rm -f conftest.* fi if test -n "$vl_cv_prog_cc_warnings"; then CFLAGS="$CFLAGS $vl_cv_prog_cc_warnings" CXXFLAGS="$CXXFLAGS $vl_cv_prog_cc_warnings" else vl_cv_prog_cc_warnings="unknown" fi ]) ])dnl AC_DEFUN([AC_C_BIGENDIAN_CROSS], [AC_CACHE_CHECK(whether byte ordering is bigendian, ac_cv_c_bigendian, [ac_cv_c_bigendian=unknown # See if sys/param.h defines the BYTE_ORDER macro. AC_TRY_COMPILE([#include #include ], [ #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN bogus endian macros #endif], [# It does; now see whether it defined to BIG_ENDIAN or not. AC_TRY_COMPILE([#include #include ], [ #if BYTE_ORDER != BIG_ENDIAN not big endian #endif], ac_cv_c_bigendian=yes, ac_cv_c_bigendian=no)]) if test $ac_cv_c_bigendian = unknown; then AC_TRY_RUN([main () { /* Are we little or big endian? From Harbison&Steele. */ union { long l; char c[sizeof (long)]; } u; u.l = 1; exit (u.c[sizeof (long) - 1] == 1); }], ac_cv_c_bigendian=no, ac_cv_c_bigendian=yes, [ echo $ac_n "cross-compiling... " 2>&AC_FD_MSG ]) fi]) if test $ac_cv_c_bigendian = unknown; then AC_MSG_CHECKING(to probe for byte ordering) [ cat >conftest.c <&AC_FD_MSG ac_cv_c_bigendian=yes fi if test `grep -l LiTTleEnDian conftest.o` ; then echo $ac_n ' little endian probe OK, ' 1>&AC_FD_MSG if test $ac_cv_c_bigendian = yes ; then ac_cv_c_bigendian=unknown; else ac_cv_c_bigendian=no fi fi echo $ac_n 'guessing bigendian ... ' >&AC_FD_MSG fi fi AC_MSG_RESULT($ac_cv_c_bigendian) fi if test $ac_cv_c_bigendian = yes; then AC_DEFINE(WORDS_BIGENDIAN, 1, [whether byteorder is bigendian]) BYTEORDER=4321 else BYTEORDER=1234 fi AC_DEFINE_UNQUOTED(BYTEORDER, $BYTEORDER, [1234 = LIL_ENDIAN, 4321 = BIGENDIAN]) if test $ac_cv_c_bigendian = unknown; then AC_MSG_ERROR(unknown endianess - sorry, please pre-set ac_cv_c_bigendian) fi ]) AC_DEFUN([KSW_HAVE_GETTIMEOFDAY], [ AC_REQUIRE([AC_PROG_CC]) AC_CACHE_CHECK([for gettimeofday], ksw_cv_have_gettimeofday, [ cat > ksw_have_gettimeofday_test.c << EOF #include #include int main( int argc, char **argv ) { struct timeval tv; gettimeofday( &tv, NULL ); return 0; } EOF if ${CC} -c ksw_have_gettimeofday_test.c 2> /dev/null > /dev/null; then have_gettimeofday=yes else have_gettimeofday=no fi rm -f ksw_have_gettimeofday* ksw_cv_have_gettimeofday="$have_gettimeofday" ]) have_gettimeofday="$ksw_cv_have_gettimeofday" if test x"$have_gettimeofday" = "xyes"; then AC_DEFINE( [HAVE_GETTIMEOFDAY], [], [Define when you have gettimeofday function] ) fi ]) AC_DEFUN([KSW_IS_OSX], [ AC_REQUIRE([AC_PROG_CC]) AC_CACHE_CHECK([for OS X], ksw_cv_is_osx, [ cat > ksw_is_osx_test.c << EOF #include int main( int argc, char **argv ) { return 0; } EOF if ${CC} -c ksw_is_osx_test.c -framework Carbon 2> /dev/null > /dev/null; then is_osx=yes else is_osx=no fi rm -f ksw_is_osx* ksw_cv_is_osx="$is_osx" ]) is_osx="$ksw_cv_is_osx" if test x"$is_osx" = "xyes"; then AC_DEFINE( [IS_OSX], [], [Define when you run on OSX] ) fi ]) AC_DEFUN([ZTM_WITH_MACOSX_VERSION_MIN], [ AC_REQUIRE([AC_PROG_CC]) AC_ARG_WITH([macosx-version-min], [ --with-macosx-version-min=VERSION Minimum Mac OS X deployment VERISON (i.e., 10.10), It should not be lower than Qt's macosx-version-min.]) if test x"$with_macosx_version_min" != x; then ztm_macosx_major=`echo $with_macosx_version_min | cut -d. -f1` ztm_macosx_minor=`echo $with_macosx_version_min | cut -d. -f2` if test $ztm_macosx_minor -gt 9; then # Do math and then remove decimal. 10.10 -> 101000.0 -> 101000 MAC_OS_X_VERSION_MIN_REQUIRED=`echo "$ztm_macosx_major * 10000 + $ztm_macosx_minor * 100" | bc` # | cut -d. -f1 else # Multiply by 100 and then remove decimal. 10.7 -> 1070.0 -> 1070 MAC_OS_X_VERSION_MIN_REQUIRED=`echo "$with_macosx_version_min * 100" | bc` # | cut -d. -f1 fi LDFLAGS="$LDFLAGS -mmacosx-version-min=$with_macosx_version_min" CFLAGS="$CFLAGS -mmacosx-version-min=$with_macosx_version_min -DMAC_OS_X_VERSION_MIN_REQUIRED=$MAC_OS_X_VERSION_MIN_REQUIRED" CXXFLAGS="$CXXFLAGS -mmacosx-version-min=$with_macosx_version_min -DMAC_OS_X_VERSION_MIN_REQUIRED=$MAC_OS_X_VERSION_MIN_REQUIRED" MACOSX_DEPLOYMENT_TARGET=$with_macosx_version_min AC_SUBST(MACOSX_DEPLOYMENT_TARGET) fi ]) mm3d-1.3.15/autogen.sh000077500000000000000000000002101466047437300144510ustar00rootroot00000000000000#!/bin/sh DIR=$(dirname $0) if [ ! -z "$DIR" ]; then cd "$DIR" fi aclocal && autoconf && automake --add-missing && touch config.h.in mm3d-1.3.15/cleanup.sh000077500000000000000000000001051466047437300144410ustar00rootroot00000000000000#!/bin/sh rm `find . -iname "*.base.*"` rm `find . -iname "*.moc.*"` mm3d-1.3.15/config.guess000077500000000000000000001263731466047437300150130ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-24' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval "$set_cc_for_build" cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "$HP_ARCH" = "" ]; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ "$HP_ARCH" = hppa2.0w ] then eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; i*86:Minix:*:*) echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) if objdump -f /bin/sh | grep -q elf32-x86-64; then echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32 else echo "$UNAME_MACHINE"-pc-linux-"$LIBC" fi exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval "$set_cc_for_build" if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: mm3d-1.3.15/config.h.generic000066400000000000000000000006561466047437300155170ustar00rootroot00000000000000#define PACKAGE "mm3d" #define HAVE_QT5 1 /* Little Endian */ /* Only define if not already defined, for src/test/libmm3d/big_endian_test.cc */ #ifndef BYTEORDER #define BYTEORDER 1234 #endif #ifndef WIN32 /* Define when you have dlopen function */ #define HAVE_DLOPEN /* Define when you have gettimeofday function */ #define HAVE_GETTIMEOFDAY /* Define when you have Lua libs installed */ /* #define HAVE_LUALIB */ #endif mm3d-1.3.15/config.h.in000066400000000000000000000200501466047437300144770ustar00rootroot00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if the `closedir' function returns void instead of `int'. */ #undef CLOSEDIR_VOID /* Define to include debugging information */ #undef CODE_DEBUG /* Define to include core profiling information */ #undef CORE_PROFILE /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_DIRENT_H /* Define when you have dlopen function */ #undef HAVE_DLOPEN /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT /* Define to 1 if you have the `fork' function. */ #undef HAVE_FORK /* Define to 1 if you have the `ftime' function. */ #undef HAVE_FTIME /* Define to 1 if you have the `getcwd' function. */ #undef HAVE_GETCWD /* Define when you have gettimeofday function */ #undef HAVE_GETTIMEOFDAY /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if `lstat' has the bug that it succeeds when given the zero-length file name argument. */ #undef HAVE_LSTAT_EMPTY_STRING_BUG /* Define when you have Lua installed */ #undef HAVE_LUA /* Define when you have Lua libs installed */ #undef HAVE_LUALIB /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #undef HAVE_MALLOC /* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the `mkdir' function. */ #undef HAVE_MKDIR /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the `pow' function. */ #undef HAVE_POW /* Define when you have QT5 installed */ #undef HAVE_QT5 /* Define to 1 if your system has a GNU libc compatible `realloc' function, and to 0 otherwise. */ #undef HAVE_REALLOC /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH /* Define to 1 if you have the `sqrt' function. */ #undef HAVE_SQRT /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ #undef HAVE_STAT_EMPTY_STRING_BUG /* Define to 1 if stdbool.h conforms to C99. */ #undef HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR /* Define to 1 if you have the `strcspn' function. */ #undef HAVE_STRCSPN /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strncasecmp' function. */ #undef HAVE_STRNCASECMP /* Define to 1 if you have the `strrchr' function. */ #undef HAVE_STRRCHR /* Define to 1 if you have the `strspn' function. */ #undef HAVE_STRSPN /* Define to 1 if you have the `strstr' function. */ #undef HAVE_STRSTR /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_DIR_H /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIMEB_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have that is POSIX.1 compatible. */ #undef HAVE_SYS_WAIT_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `vfork' function. */ #undef HAVE_VFORK /* Define to 1 if you have the header file. */ #undef HAVE_VFORK_H /* Define to 1 if you have the `vprintf' function. */ #undef HAVE_VPRINTF /* Define to 1 if `fork' works. */ #undef HAVE_WORKING_FORK /* Define to 1 if `vfork' works. */ #undef HAVE_WORKING_VFORK /* Define to 1 if the system has the type `_Bool'. */ #undef HAVE__BOOL /* Define when you run on OSX */ #undef IS_OSX /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ #undef LSTAT_FOLLOWS_SLASHED_SYMLINK /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define installation prefix */ #undef PREFIX /* Define to include profiling information */ #undef PROFILE /* Define as the return type of signal handlers (`int' or `void'). */ #undef RETSIGTYPE /* Define to 1 if the `S_IS*' macros in do not work properly. */ #undef STAT_MACROS_BROKEN /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME /* Version number of package */ #undef VERSION /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to the type of a signed integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef int16_t /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef int32_t /* Define to the type of a signed integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef int8_t /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc /* Define to `int' if does not define. */ #undef mode_t /* Define to `int' if does not define. */ #undef pid_t /* Define to rpl_realloc if the replacement function should be used. */ #undef realloc /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef uint16_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t /* Define as `fork' if `vfork' does not work. */ #undef vfork mm3d-1.3.15/config.sub000077500000000000000000001064501466047437300144500ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo "$1" | sed 's/-[^-]*$//'` if [ "$basic_machine" != "$1" ] then os=`echo "$1" | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2*) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsv-tandem) basic_machine=nsv-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; x64) basic_machine=x86_64-pc ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases that might get confused # with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) -es1800*) os=-ose ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ | -midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -xray | -os68k* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4*) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $basic_machine in arm*) os=-eabi ;; *) os=-elf ;; esac ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac echo "$basic_machine$os" exit # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: mm3d-1.3.15/configure.ac000066400000000000000000000100131466047437300147400ustar00rootroot00000000000000AC_PREREQ(2.61) dnl If you change the version, also change the following files dnl src/mm3dcore/version.h (for x.x.x) dnl src/win_resource.rc (for x.x.x) dnl mm3d-win32-installer.nsi (for x.x.x) dnl desktop/moe.clover.mm3d.metainfo.xml (for x.x.x) dnl Makefile.generic (for x.x.x) dnl Makefile.am (for Contents/PlugIns/mm3d/x.x) dnl plugins/Makefile.am (for plugins/x.x) AC_INIT([mm3d], [1.3.15]) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_RANLIB KSW_IS_PROFILE KSW_IS_DEBUG ZTM_WITH_MACOSX_VERSION_MIN # Checks for libraries. # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([limits.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/time.h sys/timeb.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STAT AC_HEADER_STDBOOL AC_C_CONST AC_C_INLINE AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT8_T AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_CLOSEDIR_VOID AC_FUNC_ERROR_AT_LINE AC_FUNC_FORK AC_FUNC_LSTAT AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK if test "$cross_compiling" = yes; then # Assume the platform provides a working malloc() when cross-compiling. # Otherwise autotools would set HAVE_MALLOC and HAVE_REALLOC to 0 and # defines malloc and realloc to (non-existant) rpl_malloc and rpl_realloc. AC_DEFINE_UNQUOTED( [HAVE_MALLOC], [1] ) AC_DEFINE_UNQUOTED( [HAVE_REALLOC], [1] ) else AC_FUNC_MALLOC AC_FUNC_REALLOC fi AC_FUNC_MEMCMP AC_TYPE_SIGNAL AC_FUNC_STAT AC_FUNC_VPRINTF AC_CHECK_FUNCS([ftime getcwd gettimeofday localtime_r memmove memset mkdir pow realpath sqrt strcasecmp strchr strcspn strdup strerror strncasecmp strrchr strspn strstr]) KSW_HAVE_LUA KSW_HAVE_LUALIB KSW_IS_OSX KSW_HAVE_DLOPEN MDL_HAVE_OPENGL BNV_HAVE_QT KSW_HAVE_GETTIMEOFDAY dnl AC_C_BIGENDIAN_CROSS( AC_DEFINE( MM3D_BIGENDIAN ), AC_DEFINE( MM3D_LITTLEENDIAN ), AC_DEFINE(MM3D_UNKNOWNENDIAN) ) if test x"$prefix" != xNONE; then AC_DEFINE_UNQUOTED( [PREFIX], ["$prefix"], [Define installation prefix] ) else AC_DEFINE_UNQUOTED( [PREFIX], ["$ac_default_prefix"], [Define installation prefix] ) fi # for cross-compiling # CC_FOR_BUILD is used for compiling build utils that run on the build host if test x"${AR}" = x; then AR=ar fi if test x"${RANLIB}" = x; then RANLIB=ranlib fi AC_SUBST(AR) AC_SUBST(RANLIB) if test x"${CC_FOR_BUILD}" = x; then CC_FOR_BUILD=${CC} CFLAGS_FOR_BUILD=${CFLAGS} LDFLAGS_FOR_BUILD=${LDFLAGS} fi AC_SUBST(CC_FOR_BUILD) AC_SUBST(CFLAGS_FOR_BUILD) AC_SUBST(LDFLAGS_FOR_BUILD) AC_SUBST(QT_LIBS) AC_SUBST(QT_CXXFLAGS) AC_SUBST(QT_CXXFLAGS) AC_SUBST(DLOPEN_LIBS) AC_SUBST(HAVE_QT5) AC_SUBST(BYTEORDER) AC_SUBST(PROFILE) AC_SUBST(CORE_PROFILE) if test x"${QT_LIBS}" = x; then echo "" AC_MSG_ERROR([Qt is required. If you have Qt installed see --help for more options]) echo "" fi if test x"$have_GLU" != xyes; then echo "" AC_MSG_ERROR([OpenGL (with GLU) is required.]) echo "" fi if test x"${ksw_cv_qgl_test_result}" = xfailure; then echo "" AC_MSG_ERROR([Qt with OpenGL is required.]) echo "" fi AC_CONFIG_FILES([ Makefile src/Makefile src/libmm3d/Makefile src/mm3dcore/Makefile src/depui/Makefile src/qtui/Makefile src/qtui/images/Makefile src/implui/Makefile src/tools/Makefile src/commands/Makefile src/pixmap/Makefile plugins/Makefile man/Makefile translations/Makefile desktop/Makefile doc/Makefile doc/html/Makefile doc/html/olh_images/Makefile doc/html/olh_images/screencaps/Makefile doc/html/olh_images/tools/Makefile util/Makefile ]) AC_OUTPUT if test x"${have_dlopen}" != xyes; then echo "" echo "Warning: dlopen was not detected. You can contiue with the" echo "install, but plugins will not work." echo "" fi mm3d-1.3.15/depcomp000077500000000000000000000560201466047437300140370ustar00rootroot00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2020 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: mm3d-1.3.15/desktop/000077500000000000000000000000001466047437300141305ustar00rootroot00000000000000mm3d-1.3.15/desktop/Makefile.am000066400000000000000000000025311466047437300161650ustar00rootroot00000000000000EXTRA_DIST = \ moe.clover.mm3d.desktop \ moe.clover.mm3d.metainfo.xml \ moe.clover.mm3d.mm3dmodel.xml \ moe.clover.mm3d.png \ moe.clover.mm3d-64.png \ moe.clover.mm3d-32.png \ moe.clover.mm3d-16.png all: install: $(INSTALL) -d $(DESTDIR)$(datadir)/applications/ $(INSTALL) -d $(DESTDIR)$(datadir)/metainfo/ $(INSTALL) -d $(DESTDIR)$(datadir)/mime/packages/ $(INSTALL) -d $(DESTDIR)$(datadir)/icons/hicolor/128x128/apps/ $(INSTALL) -d $(DESTDIR)$(datadir)/icons/hicolor/64x64/apps/ $(INSTALL) -d $(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/ $(INSTALL) -d $(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/ ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d.desktop $(DESTDIR)$(datadir)/applications/ ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d.metainfo.xml $(DESTDIR)$(datadir)/metainfo/ ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d.mm3dmodel.xml $(DESTDIR)$(datadir)/mime/packages/ ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d.png $(DESTDIR)$(datadir)/icons/hicolor/128x128/apps/moe.clover.mm3d.png ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d-64.png $(DESTDIR)$(datadir)/icons/hicolor/64x64/apps/moe.clover.mm3d.png ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d-32.png $(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/moe.clover.mm3d.png ${INSTALL} -m 0644 $(srcdir)/moe.clover.mm3d-16.png $(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/moe.clover.mm3d.png mm3d-1.3.15/desktop/moe.clover.mm3d-16.png000066400000000000000000000010271466047437300177720ustar00rootroot00000000000000PNG  IHDRasBIT|dIDAT8=kQߝu7uhl&,ll`'!BA㊑׍\l|i( &xik^[W ]p+3WyUG]ѳ;_Jzͱ2ƞ̟nRe(ktL+=Uzsa KN]ZP TOE' CBי5tw)xR` 7*5uZAw`⮱_&u q~^Xn$sҺ 4w3toW/> _=ؾ%hh[KކsGJ҂mџmɛ>.ꊑKǒL(,L nfH'ӕX]|?ɦ:j5\k2_2n8LYg(:oʝe5[TU # ]7t F>ZxKnS׮u{]y=;jf7 4Vm>vԇ 7 0t;vFy},0Dn)/\]X%tj¾Rus~J]~ƹ a4IENDB`mm3d-1.3.15/desktop/moe.clover.mm3d-64.png000066400000000000000000000045321466047437300200010ustar00rootroot00000000000000PNG  IHDR@@iq !IDATx[dGܒf3F٨A |M#I7bF/ADQAݬh&!Ydٙٝӧ|lkq)jx{= FKI VO%_C FcP" m 6=DayDz%ovX6LTB@yGw@H*Пф5( 17@]@MS L: -bh+#%(~<qPӢ5ƴ|d@Z@iX_.uaP)`ThDSM*6Wcw}XZ9,ϩfАjpFŴI`7p X&JV<>̐2!OP}srց8x-Y^6x"e\|.|bۿmu%dIKL (Rwp1mQ7Qˈ:W >L.U3Bň&öwU CII %M?fILrdO=ƚб5s_QFiPN,q!Hwc*g>3qiˆ%*$o;cڶ)IE #CJӌ\&t40jz?ӠzI.qdxllQ}y_4H - V},R m6#4}MFfaWwv\ؘ,u rTR&0CYKEǁv. #OԤ 8ͧ:4ԭ,ST$DTlq0;T Sq130 e! 3î.qj&xQ34MZ YDq$Ѧ"0r euEk)MY8' Ǧ(^\p^ RѲڅrrlgL sPG+@lf1!>&T loH, (O,0B!6Uj'"V`}o~['E +`Q2Æ Fj)bf<6,̇H8]\֨r96I:XvQE[0xV,*lҡ%|$j9Y嵩}p-28m 4 *Ӭ2K7 _Xg>|^ȌC M. 7[׀sÆ֣k;6cnc4|Rw4j7Kb x9( g$wRc R4297J6 hk7Lu' &N0|,2e稢ݑn4u;=&NquJ'R 8Um-1k&\?6ץ)y͑k,?g/pԑ*XImnSk 9˅ 9,OPTdBDΰ~_ȼ,hX2^P9H9ɸʸ#i1[f# |θ*[~ g^D͐q峤Tryn@lgefok 'ť7]*֙<,ǻUlN"GzaHW94-Eo:&Je BP ˑ*#_LpflT=b,3|S+/8MVsN {8.SN o2 8ͣ\ImM6-3ٺH5,\np Y$&cR..^½mP \*x^ms5C2S$|Q6d}!4&&2LmJ}kEFgMYm@sP0n=Au`*ual11`/[:g3 @VցeS" moe.clover.mm3d Maverick Model 3D 3D model editor and animator for games CC0-1.0 GPL-2.0-or-later

Maverick Model 3D is a triangle-based 3D model editor that supports a variety of geometry manipulation tools, texturing, and skeletal and vertex frame animations. There is a help manual included.

It supports several 3D model formats; Misfit Model 3D .MM3D, Wavefront .OBJ, Milkshape .MS3D, Quake engine formats .MD2 and .MD3, and GameMaker .D3D. There is export-only support for GoldSrc/Source .SMD and Inter-Quake Export .IQE. There is unmaintained support for LightWave 3D Object (lwo), Caligari trueSpace (cob), and AutoCAD (dxf).

https://raw.githubusercontent.com/clover-moe/screenshots/61fa0b6616ad97b7ba39fb222b00abb1f84d2aa2/mm3d/1.png moe.clover.mm3d.desktop https://clover.moe/mm3d https://github.com/zturtleman/mm3d/issues https://github.com/zturtleman/mm3d zturtleman zturtleman@gmail.com mm3d mm3d.desktop ModernToolkit UserDocs mm3d

This adds Quake 3 player export to IQE format, fixes Linux Dark Mode and issues on 2023 Flatpak runtime, and fixes not updating frame count in animation mode until focus changes.

https://clover.moe/2024/08/18/maverick-model-3d-1-3-15/
  • Added HiDPI support.
  • Added Offset by Normal command for creating toon cel outline.
  • Make Select Vertex tool automatically select and unselect triangles.
  • Automatically select new face made by Make Face From Vertices.
  • Fixed many issues.
https://clover.moe/2023/04/23/maverick-model-3d-1-3-14/
  • Added GameMaker Studio .D3D model import/export.
  • Fixed Quake MD2 vertex normals orientation.
  • Allow adding and removing groups and points and removing vertexes from models with frame animations.
  • Make pasting and spliting skeletal animations keep the same pose for interpolated joints.
  • Fixed many issues.
https://clover.moe/2021/12/21/maverick-model-3d-1-3-13/
  • Added shift key behavior for tools in texture coordinate editor.
  • Added conversion of multiple skeletal animations to frame animations at once.
  • Fixed several issues.
https://clover.moe/2019/10/08/maverick-model-3d-1-3-12-released/
  • Added Source/Goldsrc .SMD model export.
  • Improved MilkShape3D .MS3D support.
  • Fixed many issues.
https://clover.moe/2019/01/02/maverick-model-3d-1-3-11-released/

Initial release branded as Maverick Model 3D.

https://clover.moe/2018/09/05/maverick-model-3d-1-3-10-released/
mm3d-1.3.15/desktop/moe.clover.mm3d.mm3dmodel.xml000066400000000000000000000006451466047437300214470ustar00rootroot00000000000000 MM3D model mm3d-1.3.15/desktop/moe.clover.mm3d.png000066400000000000000000000131511466047437300175470ustar00rootroot00000000000000PNG  IHDR>a pHYs  d_IDATx{l]?k9ý\rW6JmJkmƨiԪm>X,"HmQgk[$6 i 5(p X, OQ:Ǥlk!rߏuW,KY  OL N󜩂#wq -=f<*KG6a`x Sk@y-@bm-@ h b@v3Zc/5Vܹs#%oE~pBwÁ}Z367T3fՉ_%W蘋/?U^<S@EN_Ho_o k6ǭM['5 BBK z`'(nxCJ?OXj<<: C`B'+0bIxg)͊I.b9fk*gjXns.Hf-s˸S8A)12֜ΐC!!OWJ0tY~ȅ){@O:& 5NBo=_6s?*q-d -ЖGgGw `]A0>NJKgѸx C`=Jbc<\~s4jIM\QG~ѥrt] Vxbej#b}O#eTVة`w鍚b)?D^̈u=P)׻<r>#;;OP)=?>`P3>a@%60T(\z2 Bp!`mgm2%:b#4$9R@#;l "~zP- FP [G<~.7Zw4p;0|lvrѾ"=S uSB–E%TT䆦8`D¸"ҽ~w1?*rHKUaRC%8MR鿺&-:8AQեʔ:لJ:!bN4?-)6p\'~OQx6䜁5 5ISØ[~{jOKejB\8NO0|B%ڋU|BLc,}),s%?%z5+oi *2O|JI9EO bP sp?|ى Xү9hhфB!b]ߔ}(3}; (B@/`u6ùBz '91 OX(|R:"@#,C3@%geUW_F|𜼥sN=j= "A@ث*X9eڗo"š$\(e+=lR^apboEjc fCl.n41s\ү, P|%.RSt:{+NM_}g?u9_/@;˦%tz5Rh\*"9z%$o{ˀw=_Rbc*7roـ/9V|˾D\xWH*8|F3 OW>=|SM ޙm*n1VΜƟ!*||mUx@a?BzdcuCbO_6,0J0_jJKT1vꀞ#ֲ"^$GDlsW 9cr4pӀ-)&<.;u, 5gNk4/<@KæWK̍q宁ȶGU+kHF#PB@@:"$&Xey3c5Wg2=?s'yWh=acXIQoL`hHW!ם~Cө/2-,^χ*kv~6MF:{V^.s=-"nKjƖ'buUn'MV@xP"%Ym\9|U 셦楥f}.` ŒY')`-ΦnO#(zh/<ŏ͙{h.Xc<;y#mt4X"z⿏c? p|/s>G9o@<%x-x-s |UJ(l"^k SuRѣarjS ԍ7m6;xgqj%6i8$u15qo/YWO?O֨| SSnzcSWgќj~*8|XR9c䀐>mPd& (Xb~ P;\Ӽ{]NDotUIoƲ@ Q`yܴsL'dI6>ꯝs~Ptho3~C v('Tm] 5)s~ `u p{9tj2@J{ݵ-F/޾OiWW:1tJd&T6;=2;SY'WY $*.html install: all ${INSTALL} -d $(DESTDIR)${DOCDIR}/olh_images/screencaps ${INSTALL} -d $(DESTDIR)${DOCDIR}/olh_images/tools ${INSTALL} -m 0644 $(PAGES) $(DESTDIR)${DOCDIR} ${INSTALL} -m 0644 $(srcdir)/olh_images/screencaps/*.png $(DESTDIR)${DOCDIR}/olh_images/screencaps ${INSTALL} -m 0644 $(srcdir)/olh_images/tools/*.png $(DESTDIR)${DOCDIR}/olh_images/tools ${INSTALL} -m 0644 $(srcdir)/olh_images/tools/*.jpg $(DESTDIR)${DOCDIR}/olh_images/tools CLEANFILES = ${PAGES} mm3d-1.3.15/doc/html/TODO000066400000000000000000000012671466047437300146660ustar00rootroot00000000000000TODO: grep FIXME Group Clean-up Window Selected dimensions in context panel Tutorial UI Tour (main window documentation) x Basic Modeling x Primitive Creation x Manipulations x Extrude, split, turn x Background Images ? Polytool / Free Vertices / Make Face Groups and Textures x Grouping Texturing / Materials x Projections x Coord edit x Texture paint x Texture reload Skeletal structure and Animations x Bone Joints x Influences Animations Points / uses MD2/MD3/Cal3d guides Links to other modeling tutorials mm3d-1.3.15/doc/html/olh_alignwin.htm000066400000000000000000000035561466047437300173650ustar00rootroot00000000000000

Align Selection Window

The Align Selection Window moves the selected portion of the model so that the minimum, maximum, or center values of a given coordinate axis are aligned along a specified value of the axis. For example, if you have a cube you can use the Align Selection window to align the left or right (minimum or maximum, respectively) side of the cube with the axis (0.0) or any other value for X. The shape of the selected portion of the model remains unchanged.

Tip: If all you want to do is align the center of the selected geometry to a specific location, it may be easier to use the Position Properties panel.

The Align Selection Window is composed of three frames, one for each model dimension. Each frame contains a text entry box to enter a numerical value and three radio buttons for aligning the minimum, center, and maximum vertex values with the numerical value. The Align Now buttons change the alignment of the selected model region.

Alignment values are as follows:

  minimum center maximum
X left center right
Y bottom center top
Z back (far) center front (near)

Press Ok to keep your changes or press Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_alignwin.page000066400000000000000000000001011466047437300174700ustar00rootroot00000000000000PAGE_TITLE=Align Selection Window PAGE_CONTENT=Animation Details

Overview

You can create animations to define how your model moves over time. Maverick Model 3D allows you to use bone joints to create skeletal animations or to set vertex positions exatly with mesh deformation animations (called "Frame" animations in Maverick Model 3D).

Animations have one or more frames which are consecutive points in time. Each frame lets you specify what the model should look like at that time. The frame rate, or FPS for "Frames Per Second", indicates how many frames are shown in one second when the animation is running at full speed.

Creating an Animation

Select "Animation | Start Animation Mode...". The Animation Panel will appear at the bottom of the screen. Select <New Animation> from the drop-down menu. Enter a name for your new animation and then select "Skeletal" for a skeletal animation or "Frame" for a mesh deformation animation.

You can create multiple animations of either type. When you have animations of both types, the animation names in the drop-down selection box list all skeletal animations first, followed by all frame animations.

Animation Details

In skeletal animations you only change bone joints. Any vertices or points attached to the bone joints will move with the joints. Generally you only want to use rotations (think about how you move your arm by rotating the shoulder and elbow, not by dislocating the joints).

Any time you rotate or move a bone joint it sets a rotation or translation keyframe for that animation frame. If you want the default position to be a keyframe in that frame, you can select the joint and then select "Animation | Set Rotation Keyframe". Often you will want to do this on the first and last frame of an animation for every joint that will be affected by the animation.

In frame animations you set the position for each vertex and point in each frame. Bone joints have no effect (and are not even rendered).

You can copy and paste entire animation frames by selecting the copy or paste option from the "Animation" menu. Clearing the frame will erase all keyframes for the current frame of a skeletal animation and will restore the model to the default position for a frame animation.

See Also

mm3d-1.3.15/doc/html/olh_animationdetails.page000066400000000000000000000001041466047437300212100ustar00rootroot00000000000000PAGE_TITLE=Animation Details PAGE_CONTENT=Convert To Frame Window

The Convert To Frame Window will take the currently selected animations in the Animation Sets Window and convert them into frame animations. You will be presented with a list of the selected skeletal animations with options for their conversion. The original animations remain unmodified.

This features is useful if you have a set of skeletal animations and want to export the model as an MD2 or MD3, which only support frame animations.

Provide a name for the frame animation in the Frame Animation text box. The name can be the same as the skeletal animation.

Set the frame count in the Frame Count spin box. The frame rate will be modified so that the frame animation and skeletal animation complete in the same amount of time. In other words:

anim1_frame_count * anim1_fps = anim2_frame_count * anim2_fps

Skeletal animations by default are 30 frames per second. Frame animations in MD2s are 10 frames per second. Frame rates in MD3 are variable and controlled by animation.cfg file. Frame rates in Cal3D are assumed to be 30 frames per second when importing.

The Convert button will convert the skeletal animations to frame animations.

The Cancel button will cancel the conversion of the animations.

mm3d-1.3.15/doc/html/olh_animconvertwin.page000066400000000000000000000001101466047437300207230ustar00rootroot00000000000000PAGE_TITLE=Convert Frame To Window PAGE_CONTENT=Export Animation Window

Overview

The Export Animation Window allows you to save an animation as a series of jpeg or png files.

Exporting Animations

Source

The Source frame has an Animation combo box to specify which animation you want to export and a Viewport combo box to specify which viewport the animation should play in. The viewports are referenced by number and also indicate the direction from which the viewport is looking at the model. The viewports are numbered from left to right, top to bottom, starting at 1.

Duration

The Duration frame controls how long the animation should play. You can specify the duration in Seconds or complete animation Iterations by clicking on the appropriate radio button and entering the number of seconds or iterations in the number entry box.

Output

The Output determines how the exported animation images will be saved. You can specify the Frame Rate, the filename Format (JPEG or PNG, with or without leading zeros), and the output Directory.

Ok and Cancel

The Ok button will start the export process and close the window.

The Cancel button will close the window without exporting animations.

mm3d-1.3.15/doc/html/olh_animexportwin.page000066400000000000000000000001071466047437300205720ustar00rootroot00000000000000PAGE_TITLE=Export Animation Window PAGE_CONTENT=Animation Sets Window

Overview

The Animation Sets Window allows you to create and organize animations. You can copy complete animations, split them into separate animations, and join multiple animations into one.

The Animation Sets Window has a combo box that lists animation types, a list box that lists animations of the selected type, and a set of buttons to perform operations on animations. Many buttons operate on all the animations selected in the animation list box.

The animation type combo box allows you to select between skeletal and frame animations. When you select an animation type you will see all animations of that type listed in the animation list box. You can use the standard Ctrl-Click and Shift-Click mouse operations to select multiple animations to operate on.

For more information on animating a model, see the Animations section of the introduction.

Some operations only work on certain types of models.

Animation Operations

Up and Down

The Up and Down buttons allow you to re-order existing animations. Select the animations you want to move and click Up to move the animations closer to the top of the list or Down to move the animations closer to the bottom of the list.

New

The New button creates a new animation. It will prompt you for an animation name and whether to use skeletal or frame animation mode. It also specifies the frame count, frames per-second, and whether to loop the animation (interpolate between the first and last frame).

Edit

The Edit button opens a window to change the details for the selected animations. It can change the name, frame count, FPS, and looping. The mode cannot be changed.

Delete

The Delete button will delete all selected animations.

Copy

The Copy button will create a copy of the selected animations. A new animation is created for each selected animation and contains the same position information, frame count, and frame rate as the original animations. The newly created animations are appended to the end of the animation list.

Split

The Split button will split a selected animation into two separate animations at the frame you specify. The specified frame and all successive frames will be removed from the original animation. The new animation will begin with the specified frame and contain all successive frames.

You will be prompted for a frame number for each selected animation. The new animation will appear immediately following the animation you are splitting.

Join

The Join button will combine all the selected animations into one animation. Each animation will be appended to the one preceding it.

Merge

The Merge button will combine two or more animations into one animation of the same length. For example if you have two skeletal animations on a model with two arms and one animation moves the right arm while another moves the left arm, you can use the Merge button to have both arms move at the same time. In order to merge two animations they must have the same frame count.

This button only works for skeletal animations.

Convert To Frame Animation

The Convert To Frame Animation button will take the currently selected animations and convert them into frame animations. The original animation remains unmodified. This button is useful if you have a set of skeletal animations and want to export the model as an MD2 or MD3, which only support frame animations.

When you select this option you will be prompted by the Covert To Frame Window to provide a name for the new animation and the frame count. The skeletal animation and the frame animation will take the same amount of time to complete, so the frame rate (in frames per second) depends on the frame count of the new animation. In other words:

anim1_frame_count * anim1_fps = anim2_frame_count * anim2_fps

This button only works for skeletal animations.

Ok and Cancel

The Ok button will close the Animation Sets window and keep all of your changes.

The Cancel button will close the Animation Sets window and undo all changes you made since opening it.

mm3d-1.3.15/doc/html/olh_animsetwin.page000066400000000000000000000001021466047437300200370ustar00rootroot00000000000000PAGE_TITLE=Animation Sets Window PAGE_CONTENT=Animation Panel

Overview

The Animation Panel allows you to animate models. You can change frame counts and frame rates as well as set skeletal keyframes or frame vertex positions. You can also play animations from this window. For performance reasons animation playback is only rendered in perspective viewports.

While the Animation Panel is open you can use the view window's toolbar to manipulate the model in animation mode. Most selection and manipulation tools work the same as they do in model edit mode, though some (such as the rotate tool in skeletal animation mode) will behave differently. Geometry creation tools have no effect in animation mode.

The behavior of various tools in animation mode is described in the Tools documentation.

The Animation Panel has a drop down box that lists all the animations that are defined for the model. It lists all Skeletal animations first, then all Frame (mesh deformation) animations. All operations operate on the animation that is named in the drop down box.

You can use the frame slider to select the animation frame that you want to view or edit. All viewports on the view window will display the frame of the model you have selected.

You can use the selection tools to select vertices for Frame animations or bone joints for Skeletal animations. You can use the move or rotate tool to change the position or orientation of selected primitives.

In Skeletal animation mode the rotate tool works differently than it does in standard edit mode. The rotation only applies to bone joints, and the rotation is always centered on a joint. In other words, you are rotating the bone joint itself, not the position of the bone joint. In Skeletal animation mode, the delete command also behaves differently. The delete command will delete any translation or rotation keyframes for all the selected joints in the current frame. Keyframes in other animation frames will not be affected.

You can use Undo and Redo from the Animation Panel just as you would in the standard model edit mode. You can select Undo and Redo from the menu on the Main Window, or use Ctrl-Z and Ctrl-Y.

For more information about creating animations with Maverick Model 3D, see the Animations section of the introduction.

Animation Operations

New Animation

To create a new animation, select <New Animation> in the animation name drop down box. It will prompt you for an animation name and if the animation is a skeletal animation or a frame animation. The prompt will also allow you specify the frame count, frames per-second, and loop setting.

Edit

The Edit button opens a dialog to change the animation name, frame count, frames per-second, and loop setting.

Delete

The Delete button will delete the current animation (there is a confirmation prompt).

Frames Spin Box

The Frames Spin Box will change the frame count of the current animation. You can use the up and down arrows, or type the number directly in the box.

Frame Slider

The Frame Slider will change the current frame of the current animation. You can use this to edit the model position for any frame in the animation. The Copy Frame, Paste Frame, and Clear Frame commands also operate on the current frame.

FPS Text Box

The FPS Text Box will change the frame rate of the current animation.

Loop Checkbox

The Loop Checkbox will change whether the current animation loops when it completes or not. If the animation completes and the Loop Checkbox is checked, the animation will restart from the first frame. If the animation completes and the Loop Checkbox is not checked, the animation will stop.

If you have loop checked, skeletal animation keyframe interpolation will wrap to the first frame. This interpolation begins on the last keyframe set for a given joint and ends on the first keyframe for that joint. If you have an animation with 10 frames and a rotation keyframe on Joint A for frames 1 and 10, then the animation will interpolate from 1 to 10, and then from 10 to 1. If Joint B has keyframes in frames 3 and 7, it will interpolate from 3 to 7, and then again from 7 to 3.

Copy/Paste/Clear Frame

You can copy one keyframe to another using the Animation menu.

Play / Pause

The Play button runs the current animation at its specified frame rate. The Play button becomes a Pause during playback so that you can pause an animation and resume it later. Paused animations resume from the time at which they were paused.

Use the Stop button to stop an animation completely. Use the Loop Checkbox to control whether the animation should start from the beginning again when it completes.

Animation playback is only rendered in perspective viewports. Orthographic viewports will not animate. This is for performance reasons, and may become a configurable option at a later time.

Stop

The Stop button will stop a running animation. An animation that is stoped and then played again will play starting from the first frame.

Close

To close the Animation Panel and return to model edit mode, select "Animation | Stop Animation Mode" or click on the panel's close button.

See Also

mm3d-1.3.15/doc/html/olh_animwin.page000066400000000000000000000000771466047437300173360ustar00rootroot00000000000000PAGE_TITLE=Animation Mode Window PAGE_CONTENT=Auto-Assign Bone Joints Window

The Auto-Assign Bone Joints Window allows you to quickly assign vertices and points to bone joints. The two factors that determine which bone joint a vertex or point is assigned to are the orientation of the joint and the distance to the joint.

The Only assign to selected joints checkbox indicates if you want to assign to any bone in the model, or limit the assignments to joints that are selected. If no bone joints are selected, this checkbox is disabled.

The auto-assign feature will assign a primary bone joint influence and up to two additional influences (a child and parent of the primary joint) for a total of up to three bone joints. The Single/Multiple slider allows you to increase or decrease the range of child and parent bone joints. If the slider is completely on the Single side, a vertex can only be assigned to one joint. As the slider moves closer to the Multiple side, child and parent joints of the primary joint are more likely to be assigned.

Press Ok to auto-assign bone joints or press Cancel to leave bone joint assignments as they are.

mm3d-1.3.15/doc/html/olh_autoassignjointwin.page000066400000000000000000000001231466047437300216230ustar00rootroot00000000000000PAGE_TITLE=Auto-Assign Bone Joints Window PAGE_CONTENT=Background Image Details

Overview

A background image is an image that is projected onto an orthographic viewport to aid in creating a model. It helps the user with getting the scale and shape of meshes right by providing a visual reference. A different background image may be applied to each of the 6 pre-defined orthographic viewport directions.

Assigning a Background Image

A background image can by assigned to a viewport direction using the Select Background Image Window. After opening this window, select the tab for the direction you want, and then click the "File" button to select an image file. The image file can be any format supported by MM3D. Background image dimensions do not need to be powers of two.

Other Background Image Details

You can move and scale a background image to position it where you need it for modeling purposes. Use the move and scale background image tools for this purpose. Scaling always preserves the aspect ratio.

See Also

mm3d-1.3.15/doc/html/olh_backgroundimagedetails.page000066400000000000000000000001211466047437300223520ustar00rootroot00000000000000PAGE_TITLE=Background Image Details PAGE_CONTENT=Select Background Image Window The Select Background Image Window assigns image files to a specific viewport direction: Front, Back, Left, Right, Top, or Bottom. A different image can be specified for each direction.

Open the Select Background Image Window by selecting "Set Background Image" item from the "Model" menu.

The Select Background Image Window is composed of the direction frame, and the None and File... buttons. Use the direction tab to select which direction you want to chose a background for, and the File... button to select the image. To unassign a background image, select the view direction and press the None button.

To reposition or resize the background image in the viewport, use the Move Background Image tool or the Scale Background Image tool.

Press Ok to keep your changes or press Cancel to ignore any changes.

See Also

mm3d-1.3.15/doc/html/olh_backgroundwin.page000066400000000000000000000001161466047437300205230ustar00rootroot00000000000000PAGE_TITLE=Select Background Image Window PAGE_CONTENT=Boolean Operation Window

The Boolean Operation Window is a dockable window that allows you to combine two mesh objects into a single object. Open the Boolean Operation Window by selecting "Model | Boolean Operation..." The supported operations are listed below.

  • Fuse -- Combine overlapping meshes, any triangles that intersect will be split so that the meshes share vertices along the edges where they intersect. No faces are removed by the Fuse operation.
  • Union -- Combine overlapping meshes like the Fuse operation, then removes internal faces (faces inside the volume of either enclosed mesh.
  • Subtraction -- Removes the selected mesh and any part of Object A that is inside the volume of the selected mesh.
  • Intersection -- Removes any volume inside of Object A or the current selected mesh that is not contained within both objects
Select object A

Select Object A

Select object B

Select Object B

Fuse

Fuse

Union

Union

Subtraction

Subtraction

Intersection

Intersection

For the union, intersection, and subtraction operations to work properly, all meshes involved must be enclosed (otherwise the behavior of the face-removal step is undefined). If the meshes are not enclosed you can still use the fuse operation and manually remove the faces that must be removed.

To peform a boolean operation, select an enclosed mesh and then click Set Object A. Then select another enclosed mesh and click [op] With Selected where [op] is the name of the selected operation (the button name changes when you select an operation).

After using a boolean operation you may want to use Simplify Mesh to combine some newly created faces.

mm3d-1.3.15/doc/html/olh_boolwin.page000066400000000000000000000001021466047437300173320ustar00rootroot00000000000000PAGE_TITLE=Boolean Operation Window PAGE_CONTENT=Contents

Cal3D Support

There are few different file formats for Cal3D and several different versions for those formats. Maverick Model 3D has support for most Cal3D functionality, but some features are missing or imperfect. The following is a short list of things to be aware of when working with Cal3D files in Maverick Model 3D. Some of these items are discussed in more detail in the sections below.

  • For the primary Cal3D model file, MM3D will read files with the .cal and .cfg extensions. INI-style files are supported, XML-style files are not (except for material files).
  • For skeleton, animation, mesh, and material files binary formats are supported for versions 700-1200. XML materials are also supported. No other XML formats are supported.
  • MM3D does not support springs or LOD reduction. These attributes will be silently discarded on import.
  • Texture map materials are supported, any additional maps in the material file will be silently discarded on import.
  • MM3D assumes 30 fps and assigns the keyframes to frame numbers.
  • MM3D will read compressed animations, but will not write compressed animations.

MM3D has a Cal3D Export Options window that allows you to write meshes in a single file, or in separate files, as well as specifying whether materials should be in XML or binary format.

File Formats and Versions

.cal and .cfg

Cal3D models are made up of skeleton, animation, mesh, and material files, as well as .cal or .cfg configuration files that define how to put the final model together from the individual files. There are two file formats for the .cal and .cfg files. One style is similar to a Windows .INI file. It has section names in square brackets and key/value pairs that specify filenames for parts of the model. The other style is an XML format. Maverick Model 3D will read and write the INI style files, but not the XML format.

Some attirbutes of the configuration file will be read, preserved, and written even though MM3D does not use them. Those attributes include things like scale and rotation.

Skeleton Files (.csf)

Binary skeleton file formats 700 through 1200 are supported. XML skeleton files are not supported.

The filename of the skeleton file written by MM3D will be the same as the main .cal or .cfg file with the extension replaced with .csf.

Animation Files (.caf)

Binary animation file formats 700 through 1200 are supported. XML animation files are not supported.

There is one animation file per animation. The animation filename is is the name of the animation plus the .caf extension. If a meta data key for "animation_[animation name] is defined, the filename specified in the meta data is used instead.

MM3D tracks animations on a per-frame basis, using the frames-per-second attribute to determine at which point in time a keyframe influences the skeleton. Cal3D animations do not have an FPS attribute, only keyframe times. MM3D assumes 30 fps to assign the keyframes to frame numbers.

MM3D will read compressed animation tracks. MM3D will write animation files in any version number you specify, but regardless of the version number MM3D will always write uncompressed animation tracks.

Mesh Files (.cmf)

Binary mesh file formats 700 through 1200 are supported. XML mesh files are not supported.

The meshes in the model may be written in one mesh file, or one file for every group. If there is a single mesh file, it will be the same name as the .cal or .cfg file with the extension replaced with .cmf. If there are multiple mesh files, the files will be named after the groups with .cmf appended.

Note that LOD (level of detail) attributes and springs are not supported by MM3D. LOD and spring data will be silently discarded when mesh files are read.

Material Files (.crf and .xrf)

Binary material file formats 700 through 1200 are supported. XML versions 900 through 1000 are supported.

Material file names are the material name with .crf or .xrf appended.

Texture map materials are supported. Other additional maps that are specified will be silently discarded by MM3D.

Often Cal3D texture images are in PNG or RAW format. MM3D supports both of these formats in addition to many other formats.

mm3d-1.3.15/doc/html/olh_cal3d.page000066400000000000000000000000631466047437300166550ustar00rootroot00000000000000PAGE_TITLE=Cal3D Notes PAGE_CONTENT=Cal3D Export Options

The Cal3D Export Options window is displayed when you save a model in Cal3D format. It allows you to specify how meshes and materials should be saved.

The Save Meshes options specify whether all meshes should be saved in one file or whether a separate mesh file should be written for each group.

The Save Materials options specify whether materials should be saved in binary or XML format. Note that material files are the only Cal3d XML format supported by Maverick Model 3D.

Click Ok to save the model with the specified options or click Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_cal3dprompt.page000066400000000000000000000001011466047437300201100ustar00rootroot00000000000000PAGE_TITLE=MS3D Export Options PAGE_CONTENT=Command Line

Overview

The Maverick Model 3D command line allows you to specify models to open at start time, operations to run on those models, and other start-time configuration options.

A complete command line reference is included below.

Command Line Options

-h --help

Prints a brief summary of command line options.

-v --version

The version option prints the version information for Maverick Model 3D.

--convert

The convert option saves every model file specified on the command line in the specified format. The format is a string that is the filename extension. The existing filename extension will be replaced by the new extension to create the new filename. If a file already exists by this name it will be overwritten (if you're converting files in batches from the command line mm3d assumes you know what you're doing).

The convert option takes one argument, which is the extension that specifies the format you want to save. You can only specify this argument once. If it is used multiple times only the last format will be used.

--language

The language option specifies the language in which the UI will be displayed. If you do not specify this option, the system's default locale will be used to determine the appropriate language.

See the Maverick Model 3D website for a list of supported languages.

--no-plugins

The no-plugins option disables all plugins (this may be useful if you suspect a plugin is causing a crash on initialization).

--no-plugin

The no-plugin option disables a specific plugin. This option takes an argument which is the name of the plugin to disable (the filename, without the .so extension). Maverick Model 3D will still get the plugin information from this plugin, but will not initialize it and will report it as disabled in the Plugins Window.

--sysinfo

The sysinfo option prints system information. This is present for bug-reporting reasons. If you want to submit a bug, include the output of this option with your bug report.

--debug

The debug option prints debug log messages to standard error output. This is on by default in debug builds and off in release builds.

--warning

The warning option prints warning log messages to standard error output. This is on by default in debug builds and off in release builds.

--error

The error option prints error log messages to standard error output. This is on by default in all build configurations.

--no-debug

The no-debug option disables debug log messages.

--no-warning

The no-warning option desiables warning log messages.

--no-error

The no-error option disables error log messages.

mm3d-1.3.15/doc/html/olh_cmdline.page000066400000000000000000000000661466047437300173050ustar00rootroot00000000000000PAGE_TITLE=Command Line PAGE_CONTENT=Geometry Menu

Undo

Overview

Undo reverses the last operation (if available). The last operation could be the last use of the mouse on a model canvas, the last requested command (except in the case of undo or redo), or a set of changes within one dialog box.

Undo can be used multiple times. The number of undo items available is limited by memory.

Keyboard shortcuts

  • Ctrl-Z - Undo

Redo

Overview

Redo reverses the effects of undo (if the preceding operations were undo commands). Redo can be used multiple times.

Keyboard shortcuts

  • Ctrl-R - Redo

Copy Selected to Clipboard

Overview

Copy Selected will copy all selected faces (along with groups and textures) into an internal clipboard. This selection can then be merged with another model using the Paste command.

Keyboard shortcuts

  • Ctrl-C - Copy selection

Paste from Clipboard

Overview

Paste will merge the selected portion from a previous Copy command into the current model.

Keyboard shortcuts

  • None

Weld Vertices

Overview

Weld Vertices will join selected vertices into one vertex. The vertices must be very close to each other.

More than one set of vertices can be welded at one time. If you have a model which has been duplicated and flipped you can weld the seam between the two portions by moving the seams together, selecting all vertices along the seam, and using a single weld command. Only vertices near each other will be welded so that you can weld the entire seam in one operation.

Vertices can be disconnected again by using the unweld command.

Keyboard shortcuts

  • Ctrl-W

Unweld Vertices

Overview

Unweld Vertices will create a separate vertex for each face which uses a vertex.

Vertices can be reconnected by using the weld command.

Keyboard shortcuts

  • None

Snap Vertices Together

Overview

Snap Vertices Together will move a set of selected vertices to the same coordinates and optionally weld them together. This command has four different behavaiors:

  • Snap All Selected - All selected vertices snap to the same location, they remain separate vertices
  • Snap Nearest Selected - Every two closest selected vertices snap to the same location, they remain separate vertices
  • Snap All and Weld - All selected vertices snap to the same location, all vertices are welded into one
  • Snap Nearest and Weld - Every two closest selected vertices snap to the same location, each pair of vertices is welded into one vertex

Keyboard shortcuts

  • None

Make Face From Vertices

Overview

Make Face will create a new face from three selected vertices. You must have exactly three vertices selected for this command to operate.

Keyboard shortcuts

  • None

Select Free Vertices

Overview

Select Free Vertices will select all vertices that are not connected to a triangle. Normally vertices are deleted when all triangles connected to them are deleted. The exception to this is any vertex that was created using the Create Vertex tool.

Keyboard shortcuts

  • None

Offset by Normal

Overview

Offset by Normal opens a window to interactively move the selected vertices along the vertex normal. This only affects vertices that are connected to a triangle. See the Offset by Normal Window for details.

Keyboard shortcuts

  • None

Edge Turn

Overview

Edge Turn will take two adjacent triangles and rotate the triangles on their shared vertices.

If two triangles use vertices 1 through 4 and the edge between them connects vertices 2 and 4, Edge Turn will rotate the triangles so that vertices 1 and 3 are connected.

Before

After

Keyboard shortcuts

  • None

Edge Divide

Overview

Edge Divide will split a triangle edge into two edges. Every triangle that uses this edge will be split into two triangles. To use the Edge Divide command, select the vertices at each end of the edge.

Keyboard shortcuts

  • None

Subdivide Faces

Overview

Subdivide Faces will divide a triangle face into four triangles which share vertices.

Keyboard shortcuts

  • None

Rotate Texture Coordinates

Overview

Rotate Texture Coordinates allows you to rotate the texture coordinates of a face or group of faces. If you use the Face version, the texture coordinates are rotated among each vertex of each selected face. If you use the Group version, the texture coordinates are rotated 90 degrees around the center of the material texture.

Keyboard shortcuts

  • None

Align Selected

Overview

Align Selected opens a window to move the selected portion of the model so that the minimum, maximum, or center values of a given coordinate axis are aligned along a specified value of the axis. For example, if you have a cube you can use the Align Selection window to align the left or right (minimum or maximum, respectively) side of the cube with the axis (0.0) or any other value for X. The shape of the selected portion of the model remains unchanged.

See the Align Selection Window for more details.

Keyboard shortcuts

  • None

Simplify Mesh

Overview

Simplify Mesh combines faces that do not add detail to a shape. This command does not perform LOD reduction.

Before

After

If you have a cube where each side is made up of 8 triangles, then all of these triangles are in the same plane and many edges form a single straight line. In this case, the eight faces on each side can be reduced to two faces. Often when you use a boolean operation to combine two objects you will want to use the simplify mesh command to eliminate unecessary faces from the model.

Keyboard shortcuts

  • None

Cap Holes

Overview

Cap Holes creates faces to fill in gaps in a mesh. This command can be confused by complex geometry with multiple holes. The best way to deal with more complex shapes is to select only the faces around a single hole at a time.

After using the cap holes command you may need to use the Normals Face Out command to correct the orientation of the new faces.

Keyboard shortcuts

  • None

Spherify

Overview

Spherify opens a window to interactively morph the selected portion of the model into a spherical shape. See the Spherify Window for details.

Keyboard shortcuts

  • None

Invert Normals

Overview

Invert Normals will cause normals to extend from the opposite plane of selected faces. This is useful if some sections of the model have been manipulated in such a way that polygons are facing the wrong direction.

If you have very dark polygon faces or if some polygon faces don't show up in a different rendering engine, it may be because your normals are facing the wrong direction. In these cases, inverting normals will fix the problem.

Keyboard shortcuts

  • None

Normals Face Out

Overview

Normals Face Out will find triangles in an enclosed mesh that are facing inward instead of outward. If the mesh is not enclosed the behavior is undefined.

Before

After

If you have very dark polygon faces or if some polygon faces don't show up in a different rendering engine, it may be because your normals are facing the wrong direction. In these cases, inverting normals will fix the problem.

Keyboard shortcuts

  • None

Hide

Overview

Hide will make faces or vertices of your choice invisible. This can be useful if some faces are blocking your view of the part of the model with which you are working.

You can hide the selected portion of the model or the unselected portion of the model. When you are ready to see the complete model again you can select Unhide all to make the invisible portions visible again.

Hidden model portions cannot be selected. Any commands or tools which operate on selected portions of the model will not modify hidden model objects.

Keyboard shortcuts

  • H - Hide unselected
  • Shift-H - Hide selected
  • Shift+U - Unhide all

Delete

Overview

Delete removes selected vertices, faces, and bone joints. If a vertex is removed, any faces which rely on that vertex will also be deleted. If a face is deleted which shares vertices with other faces, the vertices and unselected faces will not be deleted. If a bone joint is deleted it will delete keyframes for that bone joint and may cause animation problems for children of the deleted joint.

Delete can be used in Animation Mode to delete a skeletal animation keyframe for selected bone joints.

Keyboard shortcuts

  • Delete - Delete selection

Flip

Overview

Flip will move selected vertices and faces so that they face a different direction. You can flip in the X, Y, or Z directions. For example, a Flip X would cause the selected part of the model to change to its mirror image from left to rigth (as viewed from the front or back viewports).

Keyboard shortcuts

  • None

Flatten

Overview

Flatten will move selected vertices so that they share the same coordinate along a specific axis. You can flatten in the X, Y, or Z dimensions. For example, a Flatten X would cause the selected vertices to all have the same X coordinate.

Keyboard shortcuts

  • None

Duplicate

Overview

Duplicate creates a copy of the current selection. The old selection is unselected and the new faces will be selected for further manipulation. This is similar to a combined Copy/Paste action.

Keyboard shortcuts

  • None

Extrude

Overview

Extrude takes faces that you have selected and extends them in a direction you specify. When those faces are extended, each edge becomes another face which is connected to the faces' original locations.

There is also an Extrude Tool.

Use the X, Y, and Z text entry boxes to specify how far faces should be offset from their current location. If the "Make Back Faces" option is checked new polygons will be created where the old ones were, but facing the opposite direction (the extruded area will be completely enclosed by the extrusion).

Keyboard shortcuts

  • Insert

Invert Selection

Overview

Invert Selection will select unselected portions of the model, and unselect selected portions of the model according to the current selection mode.

If you are in group selection mode, ungrouped faces are ignored and will never be selected.

Keyboard shortcuts

  • None

mm3d-1.3.15/doc/html/olh_commands.page000066400000000000000000000000701466047437300174660ustar00rootroot00000000000000PAGE_TITLE=Geometry Menu PAGE_CONTENT=3D Model Introduciton

The links below provide details about the purpose of different parts of a 3D model and how to manipulate them.

mm3d-1.3.15/doc/html/olh_detailsindex.page000066400000000000000000000001041466047437300203400ustar00rootroot00000000000000PAGE_TITLE=3D Model Introduction PAGE_CONTENT=Contents

Contributing

Contributions to Maverick Model 3D are encouraged. There are several ways (both coding and non-coding) that you can help this project continue. The easiest thing is simply to send me an encouraging email. Bug reports and feature requests are also appreciated.

Source code patches for bugs or new features are also welcome. In the case of patches, there are a few guidelines to follow which will help expedite the integration process.

  • Follow the coding style in the rest of the project.
  • Make a patch from the latest version. If your patch is a bug fix for the stable version, your patch should be against the most recent stable version. If your patch is a new feature or a bug fix for the development version, it should be against the SVN repository or the most recent development version.
  • Contact me before starting a complex feature. I may be close to finishing a similar feature myself, or the feature you wish to add may conflict with Maverick Model 3D's design goals. If you are interested in contributing code to Maverick Model 3D, I certainly do not want your efforts to be wasted.

You can also make contributions in the form of plugins. You can create new tools, commands, texture filters, and model filters through the plugin system. I am willing to host quality plugins on the main website, or even consider integration into the core codebase.

Non-coding contributions could come in the form of documentation or artwork; or possibly even in forms I have not considered. If you have the desire and means to contribute, contact me and I will be happy to help you get started.

Plugins

Plugins can be created to add new tools, commands, texture filters and model filters. Maverick Model 3D development is done in C++. If you have a compelling reason to implement a plugin in another language, contact me and I may be able to arrange C bindings which can be extended to support other languages.

The details of the plugin system may change. The discussion below details the plugin system as of 1.4, but the development source code is always the most up-to-date source of documentation. Check the Misfit Model 3D plugins webpage for examples. The ImLib2 Texture plugin contains a documented, non-trivial example.

A Maverick Model 3D plugin is a dynamic library which contains three required functions and two optional functions, declared as PLUGIN_API (defined in src/mm3dcore/pluginapi.h). Functions marked with a * are optional.

  • plugin_init - Initialize the plugin
  • plugin_uninit - Uninitialize the plugin
  • plugin_mm3d_version - Provide Maverick Model 3D version string the plugin was compiled for
  • plugin_version * - Provide plugin version string
  • plugin_desc * - Provide plugin description string

The contents of the init and uninit functions will depend on what the purpose of the plugin is. In most cases it will register a new tool, command, or filter through the appropriate manager singleton. The version and description functions are optional, but are useful in providing information to the end user about the version and purpose of the plugin. These details can be viewed from the Plugins Window.

The plugin_mm3d_version function should return the VERSION_STRING constant that is defined in src/mm3dcore/version.h.

See the ImLib2 Texture plugin for an introduction to writing a plugin. The example also contains pointers to other files in the codebase which contain classes to derive from and documentation on how to use the manager singletons. The source code for the latest stable or development release is always the authoritative reference. I try to keep documentation in the relevant header files current.

mm3d-1.3.15/doc/html/olh_develop.page000066400000000000000000000000651466047437300173270ustar00rootroot00000000000000PAGE_TITLE=Development PAGE_CONTENT=Face Details

Overview

A face is a polygon defined by connecting vertices in 3D space. In Maverick Model 3D, all polygons are triangles. Any geometric shape that is more complex than a triangle is composed of multiple triangles. Faces may be drawn as filled polygons or as wireframes, depending on settings in the View menu.

All faces are one-sided. When a triangle is drawn as a filled polygon, the side that faces the camera is drawn in a light gray color. The side that faces away from the camera is drawn as dark gray.

Some renderers do not draw polygons that face away from the camera (this is known as back-face culling). In Maverick Model 3D, you can enable or disable drawing of polygons that face away from the camera.

A face's "normal" is a line that is perpendicular to the plane of the polygon. This line defines how light reflects off of a polygon, and also helps the renderer determine if the polygon is facing the camera or not. If faces are connected to each other, the normals can be averaged to make the edges where they connect appear smooth. You cannot directly manipulate a face's normal, but you can influence the averaging calculations by assigning faces to a group and then changing the group properties. See the Group Window for details.

Creating a Face

Faces are created implicitly when more complex geometric shapes are created. Select any tool that creates a geometric shape (such as a cube, sphere, or cylinder) and it will create new triangles which combine to form the desired shape.

Other Face Details

To increase the level of detail, one face can be subdivided into four faces using the Subdivide Faces command.

See Also

mm3d-1.3.15/doc/html/olh_facedetails.page000066400000000000000000000000721466047437300201330ustar00rootroot00000000000000PAGE_TITLE=Face Details PAGE_CONTENT=Group Details

Overview

A group is a collection of triangles that have been assigned a common name. A group can be part of a mesh, or composed of multiple meshes. Groups may have materials applied to them.

Creating a Group

A group can be created using the Groups Window or the Group Properties panel. To create a group from the Groups Window click the "New" button. To create a group from the Group Properties, select one or more faces and then select <New> from the Group drop-down menu.

Other Group Details

To apply a texture map to a group, you must first create a material. You can do this using the Materials Window. All faces in a group share the same material. A material may be used by more than one group.

To change the way the texture is mapped onto the faces of the group you will need to move the Texture Coordinates Window. Sometimes using a Texture Projection gives you a good starting point for texture coordinate assignment.

See Also

mm3d-1.3.15/doc/html/olh_groupdetails.page000066400000000000000000000000741466047437300203730ustar00rootroot00000000000000PAGE_TITLE=Group Details PAGE_CONTENT=Groups Window

The Groups Window is used to add or remove model groups (collections of faces) You may also set which faces are in which groups and assign textures. Select Edit Groups from the Main Window's Materials menu.

The combo box at the top of the window contains the names of all groups in this model. You can select a group name and use the face assignment buttons (Assign As Group and Add To Group) to set which faces belong in this group. You may also use the Select Faces In Group button to select the faces that belong to the group (this will clear any selected vertices and faces) and the Unselect Faces In Group button to unselect (other selected faces and vertices will remain selected).

To add a new group click the New button and enter a name (names do not need to be unique, though duplicate names may be confusing for other people using this model). The name is limited to 31 characters. To remove a group from the model, select the group name in the combo box and click the Delete button. Note that deleting a group in this window does not delete any faces that are in the group. This only deletes the group name. To rename a group click the Rename button.

The Smoothness slider allows you to change how smooth the surface of the polygons of the group appear (this does not change the position of vertices, it only affects the normals used for lighting calculations). A smoothness of 0 will show all the polygons as flat polygons. A smoothness of 100 will make the polygons appear as smooth as possible in viewports with smooth polygon shading enabled.

The Max Angle slider allows you to change specify the maximum angle that will automatically be smoothed. Faces that share an edge at an angle greater than this angle will be drawn as a sharp edge. Faces that share an edge at an angle less than this angle will be drawn as a rounded edge (this does not change the position of vertices, it only affects the normals used for lighting calculations).

To assign a texture to a group, select the group name in the group combo box and select a texture name in the texture combo box. A preview of the texture image will be displayed in the frame below the texture combo box. You can also use the Group Properties panel to assign a material to a group without opening the Groups Window.

Press Ok to keep your changes or press Cancel to ignore any changes.

See Also

mm3d-1.3.15/doc/html/olh_groupwin.page000066400000000000000000000000701466047437300175370ustar00rootroot00000000000000PAGE_TITLE=Groups Window PAGE_CONTENT=}~ٳXj5|a{QVVYcEelsBB7. Zi={G~`ժhݦ[c{a پ=h<#$lعs~[w3ى#]dMkB1@!B K!ad֯Ѣy"z/T⯰!>._пlBwQ?`IX|z셽{DŽ#ѳgp^P¦[B|O3!Cn:,WaW[λ=^MTTVbaXtaU!`q)4?k\DqqOƑ#3{oߎ:^(9]j-J̜u>,:l8_ ,ʕm۶ÊW_CNy*3@!SZZ}te pM[lz;#)N:oձxMXb\^IOO@1c,ҥF܉%hڴ)OCƍE.̙3qqqa`̘9 ݻgl6a#Ξ9oi}Mpz* l̙`\ (bħбcG̚5zС0slDG7-_/ [bqI\uUf3ZHcko _`.ǪW뮇 8y$ d=hѢUp\t 6]YY^=~SZvpݵ6/9+ *++Ѿ]k(:G\Ȇ\WҾmHB \\ !͚5CYYImyjuiii8}%%0xM,C$f󓒒p8xG̙= Dž Vb9ޡٳv6̙vZGDDڮ<٣ՕME!+!$еk7|Vg-=zdBaa!ڶm 8v7k?߈-Z:Q2=xoI&@vm_+lsY+QDGBHTIaǎoq%ļys0 v/ԩS8}4O{w}u#<>@^^&NGoKh*DDDO}$YB~Il1rfϚMwy:{qd"6B 3u cK/"=}6 /@^v잽z"{4m>;պ'Bffo5)cc!.{/ys wu'2z;~ܓqۭC-gʔ>{W b< "0Aqm;?~&Lx t:uF0i34[l> ~g=6Z_Vֳzi9QQQXt9.]NHB1@!CL&DQdkB=Ab{? !:B!bB!B B!B(!B1@!B!P B!bB!B B!BZ33olu5d\!CNk։Fqk!~#l5jxY,(@8<~;v*puFHMohH ?)b+.kXi(7;7YYAix&;3tj/#.PUU[ԩhӺ5p֮]D|`4neQq1vf-=GbM8,sx7BwTUU5k=j$?& X9j4 ˲ٝO&IB!@f-ҥ郯 Gll,͛7O>ҥm,;-d2Amd$&Zrf ͓Эkgol6.]ڎҪ9Gp lB!@T'O#337o=܃\cϞ={adffq9-KiD@A$ߓ'OOO/>C!`װo^lظ D㫯sQ PP Guz:AQ]]m˲ ٌC \Fc,t(:N:Rjŋ@k̻#3g!%%MFŗ  BEUU"""??3dYFdd$\Fĺ){ؽ+Cߌn];c V;JJNa𠛐ĄXt#Q  B!@`@ee%dYFvy[ڶm YQQQ2Y\xR:ww] Z_#4k8~$Kp,8BA@(Zp^L&NP <$ h*ed:u(..ƳN8a<3ͫGyO}.E%k\l HJé̉AR~/DKX:MŁz<]J ]7uP-SUU|];cVg& X9j4 ru:]mAV|NQq 9A6;ߙ<] oEFU*>\!!Z`-ҥQSNףo߾$ ۶}oAc2,X+_Cqq1ڶmN:⭑J̜u>,:l8_Ee F _wgFE],;ٝ( Ghvf&F#o$Yqd1233!dYb޽{q9%wk?Y~>ÆaʔgxB?^`gN.h+^E jD$չsފO{YYY#uőlvn[h#G:=*鐞~+@LLבɄ,DBBAqle||v!Ν#YsE(Juu#7p對FGC گtH3Q`'iۆ*^ad6CGy `@YiEI~htll, Ю];gGEBBhm۶cǎ!֦~%B1@|eplj Նl/)fc( Z$55{drl6#??]e8=f?aF4̛2RRRqx!O{{ÞCbQ=kKAL"NepO{" ^N"IT(N[]?_FbbrssѧO#vڅga4.w݉S=ދ{ޫ)aƌӻ'$Iưa0u P @ K΀YX;vGeq{w6|n΀Zƒ54h֬$!33Ӯm6{nxFT,5g̘3f9=N2d?** K.ҥ˽*@ l&1(&}F,8F#mSZe:V˄GJwNfkMYʱQ̺{ w͵ڱ`cu\iiiDEE% ")9FфCK/Gvwm]po_9|P:3APUU ?^hԨ!Wڵk~*u=(ڶICI!ظqצt:b1LHN j㬎'Zٙ5#^SVD њҪ%&N;7p& UK\pe9ǏC-3'?{#g ^W!઎$ڍ +P3 _54.F+._ ްut }^A]t¶m_Ce|].&M  0v=캯bzWqu'ŋMN7V[HJ9 RвEFs.^3ѱC;mWW,wYGrR ڴNEL*++?BF.hǁ)Z2x"i!Kߕ P\H;qd1233!dYb޽{qk#t%K((8 : :.'d2al޴}=JJJpk\Xa={džPx?C˃?СC̙=zeKq!lظ }N qx!9(..ƢEcǷX:a 6 S|(ڶIcÉ'윽+z=f͚͑sᓵk86za20cL$&&Z?WLof=gǜsf͚aΜynGk0s$''#)) sǿ?ؗ,DV)¸qq8@ ƱsHa{ Ɖ=[NO6z4UUU 22UUUNb$ %%%xxbcXFudBjj*Az$%%[Ĝٳ~\P7 qicIRtUg٘3gNkjp\sn>~{ߏ+TxvtcW=!e D8\CE$''^5sEDFFJph۶-رcwUp=xoI&@vѬY3]vn˪ytt6ڴ)bIjd;dv`5e[K{*P<ZسgՑ;l6#??]e8=摇c@[?tѺSVbܸ񨨨/]3=%,[*DQi/⮻ (}{._ BDD0ozƌӦ%eĢE 1w|Eu9 g̀q2^wҞp9 (nm7[Hj'_oFbbrss!IӜ]v!ysDGG;-k`Wо]k~/Vu+ aMh6:=z"2гW/|@dtMgZ¸'[e;/ZAzZ+}םnϠm4FdtduLbЧwOꙁXL#$0B-,)͑X𤇫D|*Qrw!w=١d B\1hY$IBff]l6c!=|]C9߽{gc>a'_97^GVֳzҲW;|>{l/ <#** K.ҥ^#{,E1@T{2 Way, kgiOiB< Ba00zlݺ+WDZZ"##QQQHJnѣ`0@ h4"[B@~OCG'?We¸.t$I(hܸ1sΡ(дiSk=!q$ 14.3Tn@ oT$['/"]bw 'R3'!|}O !̱ as BZ{:tB(¼rှ8"G?XQ 7 0Hl_Z=P7G .GDHF*֭[pIiQQQ8_qk׮ABB"ny07n첨.u[dB8VX^{ę}Jbxhsw1Zzj`l1ų]Y ]>R?Gk>D׮1zH;h25~F`0\^dҮsb_Qq oLm{dA8F?V:sXAԎJF:FIikJm-ߐ@=g-ҥQp}$Iضk~0Gmۋ^`!V| h۶V:uL&$'}%/իPYYCnRD5ib-s"GٳfOi/;Tt$$-Y>JWDQ?Fbbrss!IS1k.$7ohU"n?|)dfQ#Ҫ9=8oa3btʛ4m݈]>Lyqqӻ'z@ll,N}¨7fWG5 (7 bΝYFv؁?č7tX!ޱeX^GVֳ8r7I|5jI}?D)|[}Ͻ.5j4rrrQT\;wa!Vݕg00oKœriEEEa/aū#22RS j0v;ǡTZtNJ;C鼉Z ='O`ʕX~=mۆ?ʕ+qT FʄCbg%f%uxX5`(E|Hxay޸qc :Fqdq 1O:t8Њג?/b7)fT8}8("ѵk7]vCLLB1&;"G+Qo߫8 F Bֱk9yc-!b.^/_㯨E !bU 3DG !D3=ȗsm`;x B@;}W`El #@ oBɱB hD j8`9X5H P myΎbt )!bx TT@zA ^)"!bhuPhVF$!@:g@&:]Ը&w=o:poZiwy A DDZz:eH;ɕsNC(y)|M"Q(-fB ;5;S nvBRT bN/3Fj pc [t I s-Ba2rrpAch>>P Ehha߄kbgFB&(#Ճ_qd7:%\6|j_bWG$đeؽ yy"Y,IDM DY @2b@2\t ɀ$DQ,C- DEdYdj? eQ!/EfA(f(CȲ Q!d@My)̒Q z0R2  C2$3` R ^P{3dj΄<Gmtf&:Q  CQH&Es~K3I^Y (Y C,:= @ d 5(@H0CۯOCNDh¾L{;lvj9F*XFAۯ,uP]O1؍W~Ltd{*אs1Ymjo?Fͮݻpa? PEvHkԵue_}j_m?9ls!.O iye9+>oEq\-Ō}nﰺ ȂP6Yrt"n/ ! pP~BԼ_P)rXgc4YW0D;7nlJAt ޖȬ{F|/ߗS{9i *b LVjiI`[, /[6a66v#ae* wMBI}NOEDr`|ν| q|q[CrRz 6؝g2Ω@kUb~} {W\t&b FN:npKcR!Q!6رeرs7~0N}{W&1{~ɓ'!;{&~0^~L}Y۷}&;*潸tw w:9ijc{Xz|qҞ^o&"ږ6rx4n{V[?o.x RZc}ΕCe4ONdB$7sZǫ+#kʳ?` )>_{N ګHMiƣʺ?UsE6ɲ*̜ۣ]tbGʲ5~{QF㣏@$$R tObFM*-Ez,]G:Q}{1<Ȳ8ZPN\|ZǠAg1(b]&JN`W]$2EߡCX~#rv}NzdO?[{#GbÆqE&R$Gb7KԁΝxk#OJ[@YY)siO'[_cljMD^ C%4hP3N'BQQQZT0,7_$I1c&3g^ YY/$>Z!f̜V̙+-_}hٲqb˗!If&3'O'v{11,@bb"-6bb'=a_TT[oV-E$t5jl#no(++QQM߭ZӧO;ǕM,ɓveY"J5XscG>^$8f&oA}_c8KND7VDX8z$#//'=m|u<ēxe+رc.^999Xe;z ̚9'OBII fwWj]w߃Qxee嘑O>YaÆchA!FO׭B:}ϜN>GB%*=@V"4Kׯ7؏+7n{!ILGΦ֭[oA>HLLTl{ ^Ngw֮8$P8PtjC^2#mJԍ r0[p-8+5]uUCƏ:'bĉv\: 2 vZp~I2׎ppo\}՘1sf̜屽7o~𾙙Ŧ~g 3w`Pr&J'T!5vAr" ND_(Oሌ_JȑGe[02B \k ]G WBh?tnJ9˶;NK+Γ۔8Ptn(K C_ ys{hah!8q=p8,B_Լܸ)ٔ&rha|xm1a|CBZ B(W(x5?_SQ]^أv,_:z}Dƒ֭0` ,ۿAZz:@Őc͆X|O2B߾uFQ}JlMPEۯ6QLZzޡVv5:s=mOlנ:*lw$EdI#~1P #z8s6. u_z3PfwRe)א΅@ :2_gTrc,BMqMt }ެX;'P hٹn{d`zY>(6qe,R3P_ ,9%#>뎉c TWbdqQ ٷw/@(TJȴf=' dYFZz bbhÁ- @5vQݺMlB1Z=@-陵C5__l$n Wc2nݿquV'^1TjPJㅅxGѶMZHm ƍ-ZbgN.~%?"VnP[r=zD 4kQP `/h_,c |M ܻ{L4 0 0` _/]ڎҪ9Gp9e;ŋ93;C6ixurl❾HNGuu5Z4OsH>~/HOKAI1O_nތyru-fv9SA֩hBeeU}lUv!uAvf2'RT8믿KQPp@z:κŊeطo/6l܄Wc|(ڶIcÉ'W>f̜T4m_7|n/Ic?\̞44k s4l;m6pÍBtt N}۷os[[^uN:Rjŋ qNl_O0s$''#)) sǿ?>W*i`7X )23k[sW9)*j?r㝖]^wmU]|m'G8|pڀٗKC$VaEE'0xM,C$r%Ǟ,.FJjձ[?bp&>%VШ՘3wvEtNBM8skqU+[m:Jג4]CqP8%,8z8wM\'e*Z1Q˵72ۻYBJ ("99ϿiN,|zhRpulRR2 Ѯ];U}K/&M۵QT;[{w]Y_㙬I^"!1h۶-رcsz<4b.G8<' <m|M ax"Ξ=:Zwx~s(((arw#1m 8^Xs1mڋ}QQQh*DDDO}ή>wEq.MQ v2@9HmS',"ZGF77'N=jRZ5Ǹ'Cج)Ϣm4FdtdqOm(#~?oZDFv]ѿ>Ԕ={&VxiTSqqӻ'z@ll,N}鵸ϝ޴] f-3?lҤ/ouVso/P=UW6Y/eYFdjIo88M(-->Ń=LCBF0^umWP>F!F&xMGעЪƮzz=h}wmM@H@ Wεp1.aL#a!p$2 sᨲv$o{,Cc.A d$AE@k6A$e" "rMOP $f:@LE= |2$H!Bue T X!"jDAd{$C$C} IYWEHYPSlt:Y DQf@SDZdȢ\S^d̐!Zjϒ` ]͙ Xm_N,d6C'!Bd $״u`oi&I!Kd%KDdH%@$QlfeH fW}@(%ϝC tug}Z(ם $/ vÇ1~8&ż~S p-Pgc]էY*z#HwO crĆ$ի݊ hDߎ>ƙ IDAT?$Wǩug+Lhi ė%eYˍҭ&$f_=`$y[Xj@hy02耒c+ȃm^'5!ܸy) P ii{SNTkۻO{:8nm VyjSs|t:m_&; V@DLr${B̓mVlW`; hT(PqJ;—5j@(IX+maL&3ѫW/mNa{IhS$IXĈdAE+f._T@R pLԜXUuP=ZBQ r@%Iܹ((-;0>|=h#"€عs':tjNٹ$!i$UzɑU{b hh_][$į|gQ\?1.^dK$ ˖.3YS-JK`0УGOt(d2!wn㏸|2ZN!CpU_TT tz=.T]Bf{pU qrdYv{luu5vC Kf왉={bq1$I´i)}5~4hvCD 9QwΧE !+|mgayOy;Z"rP֭S$4ibBMYL9[͛ѩضm5jKP\\yM4_|rs1`@nرcڵM4qf3Ο须+β,=v]8]r<"}v4j˗aE(rڍJI\հuk Pv@ O*]8֙9rF{R?#ˤHΖV*P}{?%3\7 ްT4඼?zcwψ* Q 5Jrj~~4B*zi12@9Qwi%InyrVlT53aɤCJ@hY(Y1gά+r\9Y1tPxoz D6m ***My/㪫ED0p%ܙ/܌#FB$ɲ . ))U0 $5+*/1EDFy)WjOvgۊ-z8+ޔK!@|K 3pLg:V[.^$&&E04nX3\TWcGӦMǙ$ kn=hҲs˓e8<$YpPm﮼ƍܹsΝ;p10(!a;D 5mD8@B`B ui ;w]kcǷV&I2 MF\f3"##pL:t~}ADD!}ݺ!!!j8p11͠o$+.Oɱڷ0p uzgw?`$IFë™3g Ik/؄nY2۬m0/!id 3Z" o_pPn&܁oنƍ ڭT|y3JKKz Pznuѳ4DyC;t~NjӉHNN SɣۣGۻ dF4b> W{vn^Qq@wz:B(ŗBJt02P>K}qtQ[\8@B (Aݕ }7HMMC\G,ӫ{^xw>{N;gpnA3b{V6_I#ەm_Xlv=Ka߾j۾#IKO;.~`34P|WZI*$$:{NT=9ZwfD@(~!P 3l#)8xwYmWK7{x P ՝!x8DozτDOH $bHKv1&gp$J\H! fw^w;/|laWvX#lE @  A8X94ߓ&QW @H(q#8/ w@H(!B{M@!P B {YbB# h BH`! zfM6!B1@!$0P BH= D0gB !s@H4{o !B! bB9L $Z9 @H(!B{M@!P B L $Ra!* BUҲWBbgfc !z@!>OѺMۻYBF#)[B|u6ׯcl9 !I5bP B!D30B 8! zg $Z9 @H( ;w@~^:'ع{YeȒ A'@ AA `2 &5NSE:0 % BYtڤo>]hC$@Ϟl ?gTڽ G2믿Ri1ު 2̒(BlU²,QҢ 5 zԔ#D Yt&DQ 8eԔ/$3DQY!HZe/K"!ՈoQ Z. d @1,:=jT A.3D=5Yzd2 dI d PaS$% m`@~dD!ЧO6h߾=VZpZ =c|:t8!Ѱ?n,[>Oܕ+ƞ ,ʔqrc3M@pdy(I@+h.aL4+$bm'AHj^-jjQ '== Lj$׮?_>6mچ=02H>D(H8!BmO}}<}8<.;`/гg&.ZZߏ@HhhyFbB,>m4iР&"._lO?aСhڴ)4ii& :QQQHHH}݇bL&d0|pDGGEx뭷*Bl{pͷpᅦ~(_/y~3  L+>;Vx"A|@`2PQQ/ŋ|26lh{_޽{㡇rZ-['xǎ?<֞Çq@AAv؁ܰk~ 7ѳW/L}n ~1&L|O?=|1/T*?ڭڴi%ў7w.:vZ_}$$#>V\n];#)1$cӦM8''[[od=~ҥcj'ǍCEE.\ŒС=ڶINJ!I2`2f[lۻ8);a@FqF0r " qU$I&&FF+UA(uED<#`ƈ\.DNTY]==COxc.jzTVXƝӆƎ],aby?m4ԨZ3f}+~tJ{Νe˖Y9sh4izG) p{]rmV0t>=أ:a ~Zrٓ0ؗBQpa߾+WB?ɏL&|[z/ianuwޱɤLu'ޟZZ?_|IYV;0 }eb>ؼE[z~: 'J-[BI˖EGt?B'^\>Y0Mp䑈>ϩRÆ ܹsH$'N_x\x\׹빾痖f/]TzW_}U;wԀԣG}[Ҟ={2~ I}\ 2T߸RϚ.af{K[mPOqLGGqf̸M/*N[Mq~kDNեKm߾]wV޽u?ϟ9.ukj4kl=bk}O=Ko~+͜5[:#5klӟ%H$bmK-=SJRJz_K)Ipގ]wvh֭ڳgz)˚1cޫ ZZp~y/+Vhz衇I;类N7nTeenᆢߞ_=?YfkeWy͚5[?W\}kۭg=[`Sԧ:eHmّw$Q^2G_ҩcGw{"jkkOv~vǎ{]veD_׵?iڽ{.}Y]tkEe0 mIƎ޾AYYF'xB-2e.2}ڶmhɞ W_}U^z.\!CX]j޼y:cT]]y}u3Vcjݳ5gl=Ygi¹gkuLMӴnڐݞW^YW^YfcF! ?; p0I'?d^}z4wyFJڲ# 8PGj{g{z-[>ҠA\wn$RIUU駟g}F3lQPs]۶ԱjmNπFJ27޽$iƌ\G?36m~izVU@ ѷp ^0 '2x{9=$I_~9F^x5zjȑkG@ ~fӆ6nܨ[n6vٍ7@ɔ8/J[ϟ=.m߾C;w]wԅ^d췿{I.]fj˖-ڳ'YfZu~6m䘣l.կKSK$zV7oAavWh͚5J&zuu/:tϟO?Tx\w8T?[kܸqAܞ[oUwV<mݦ?Hsyy[ON7oVtX3z&N2ys9rs=ƍkպh b >YpaB@[pꪌJ@]]ꪼQ uvƍӂ <_`A^l h v gpn\5h a9]o]]&Oɓ40]yրG< /@O u=n~Pȸ! fu @y bo dZ>/ud[0Zh֑-na @<˵7 Q8 W h a: (TLKp~`A ߁B|naBE! 2 ^[ݥЉ+tr4tr\h a W:Vjפ( 9ÄzJa 40 (&p c* tza{Z{bA@V|DJ_ڻtp40(406  wuuuSugC0ת@?G@! gy/A[V1  | S40kP8|P9P8Uh aEXr`!PUCVk@   K40NU pNB@UpNBfPrztsIDATePs0^\o7a:#na @Ӹd Uy5S4pz&S mN@:b5.Y `@U@+P5*A( `A౫֗ytb\"}sJqB<U4i;S͝36@@!E`/^ŋ{>ahO8:A(9Á=8O'4i/^1A&"@GF!E*f0b-UR t`sЙR*A!:n~d3@@8 =9`G жh aC+\oq#}uU!j (T%Zer6]`A(of@ 9 \xe%I~^tw|n: 2Pd A!$ݦmuAGyJ) tKH. 6A@6[x6+j) TPLh aࠣlufJy \ Aq6NM E![0t[XiM8`TB ~|Y뺜jcP3`ޞS nUR% ym\.<ѻ aq`4gh}*--ܣg mU1fJ7TIIJJJԯ_?INtE0 @ب.]Xf?A2qgN X[[k /V0`s`RQWXZh n N[ׯTgk׮aL%%%:ꨣ΂>y!N34.YBaTLuuuA>Ai˗/Ϩ92#o ZӻwoZTJ|I딗glCЩ~U3ԟ_cǎͨ> j9QJJT__.]K.֭vڥv ]p=a*`c L9%0f̘ʕ+>븅cEEԤjIR>}r?[o a}j0 5zlժUZns ={ݻUVV&0Pܬ={hh8@*PA964*d; ga:䓵rJ >\T*#GuJM`_a֭֬Yca`4<:}E lπ3=naBQFŸ́Æ t5 LhTX̪iii9~fa@sS ݂iӛoǎ%EkgπW̏ 鴶nݪzI,ҥ:]vUKKqaʊ+ ~̓v*ܦ gGpV"P<?Əԭ[7D"qA"/A:W8[o-رc2[[9\?kVm[YF:#TQQX,&0Խ{w[#F!O5t* AmPpȓj*$xz^*F"VYp.h4qCsI$J$.D!: <& ?{à3M-D`aA:Vee6oެ~@0cաaktI#;dNu*"K{80`}뱣:0TQQ;s3#N襽{΂ӧO4AvW(8qpIz۹@$xm'V ~P9MhTvҾ}tue*h@`ƍY_j8L J̻z|ULMww,@@`p^lBW [oP_pvg 0OeevVL@ P4h S h/mBlf|g(ȶvz:Πdgy&>*( AP l>O| K ȥ+=ykjM7q}n p ~ _5fDܾvފ\b!J 6&?{.U3z z…kq"\ 2duRb1k1ްaukmٞnؚaq50={o߾Fȥ/@x5y aܶmf9`.@eQ!ڵ5Lge 틳: ^C^{2Z!#eA1vDݚʀ߀9A!Lso_P Sl7v A<͓^^%FL ACCC#0(:ש071 3ĝg(xi 0J@@as~`\)^au+e.G 1MpA{TB6 xa;M L(pP`6:Q@eyV *aTLRw>4gt:mC`^*ba_% ~a 1۫a;@` ipxÁ[0?Lu!*sV! Ng||Y3  ^߭kU„l*a?xS*R*RSS(`;\ZS9fwl>zo8ܾ60gqG&P8  v ::]/ f]^TaJ$Vs2 Ywܡ?*U}m Ͻ0Up~NL&i0_( A~~%x A/YWܪņii~A:^s~;A"M0ӭ ,u a@&P3 ΆA?!o2Fk S3܄ ZZZxs cػwuDVp;ze n7Z^! ߫w9=`o0ዷ߮ƌ sz[ 3!o-{* @EP:m.%%%D"ύD"G\X B|M:qS9~ l*a\/LӢW?LπԀ4YطoΞ97*Ձݻ[A Z#JJJЫR6{eYQȶ*{A + :OôEԤ{f@@ ՊbA>U`.P`_ S} A ]/<&? w兝W 8p ڵѨ}YW~AUlN3 s9aKRjii*86Mx\ͮGnjy ne޽TP`_u ((e4 R)IR4aQa0ߏW@{ fU2PTjiiqQ-Ԥ&"i?a h 8Ág*CtWpN$ kd:;xa2E0v"p~ aCS|N%L&jnn* ڳgN17:>X mw X,08{r ~<[05~4@\ uq7f% 9@0F† s[n g5s="^G^`aBAmz~ @_}?%탮9=B@0vD0T_Z95Ԥ70k8:z:=x}nr ֭0~@A#LQGe XLX,t?VaA-8+îV'k2&ӟgϞt9R)\.߷WY4 aY J?}:@ ;W?zasb38 ݫzq{]0`^g#P`]Woо}ǹО]{$igkB`uάYB0e5H͛*  ]0-߿__Vv>PHz=[555V F}7:{7u*;(J+|К:p0"w~N&.000000000000c_Bş=IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_bool_fuse.png000066400000000000000000001100521466047437300246220ustar00rootroot00000000000000PNG  IHDRWbKGD pHYs  tIME Hۮ IDATxyxTE4 &!ʾ+AGYDFe0 +3 *"bŏ-(QDIBn]{oU}:ue<&9 jЁ_{{D3 ܹsxMo>~<3\=/GTTl6ny-^=zcL;{vq8QXb`Qd^ Պ3u]%K]~/Yp@A$܉ S%h֬=Ák[ǡ}FǎaX믿b{@.7ͷ6obAII Gu]n}Kp֢iӦЧw/.1PT\1M M FܟX,TUUc8Ut@{"P))~Pj_n!2.iuI-u!(//GllrhGfCRRpigΔF`X<8@lpH_999x%B !ϝ;Ѧd={H`yGqpN]٨ _8[⋝7.eGsv; Ѿ}{'Lj="|)ZVN&<8 .+z-Z@UU%:vh'zaaaW)22JN)QD! LeZ.ߐ7e 3g?֭[O曃X</({ႧvݳZX0I??D<1q,[O$%%!77׭__\M!88EX,ǹ E^^GPrFK!sZX,,$9R\N믽Jb ӧߕt+Wf`SO"//IIXicݻO~ͨwaΜS> ŊcF:tČIWeƲ%2c)x\O: ÆAUU+hнDZh㮻ܹyu 1jłiҤ Æ)oaڴ>X,pk0sl̜5[zgП;#F܍#x'KM9儆b͚uXfsNQA bXw9}@~U B K!r^}UHDAb *Ԗꫯb„ `„ R)#A@  /^x A$T# 7A  A/WwAb  r8ɓ'B4*%BO1PHCi_fF=y,ii5^&>q88X,GuE!)) aaaXWĀ3`߃gR?fCp=$vPK+9䪫sgN.Amrlr11J%,88Wv,fCO._zקxw;֭ Ǝ 폟xݎll~m3AAA"' <'6G ]Qhd@gN\! sgv? 40`v؁kky^rz ._ `p8nO1h`F8PY;GL8Γ ~I3srO%%۷/v؁{٨_{۶mC߾}Qt$Ejjf! ǹ¬Yq,7[?ن#?qM78|;ls ĊdA~4l8@9t26E#6&7p#1@@h- !Q2ޟs[DB`%((UUUy:tXf}yUb1PSSyyu]tkʕ7ok!<"d1N*AQq)N!6M $Hԏѿw\o8gp,7vsO<8C^^%=BxcFTUUsy ̘> ~m⡇&≹ӧGb})A :&Q`疚&bF]h/ي]C!66ٸ;yf޽;yyؿ?[*g@gVŽ>C6tzqnL@Rbk,]o@@@|۷ƎĄV2 OBmJd8n,"ާB-o8 <C X,y{G~ر=N/ߣGOoM41cb̘^s59k6fΚMB 1b}"m# ;;wfG6m*#.Ǝ'/@ L6BA &H+j* *cٙGC}8XVb8< Q]]-[t-#~wB9A $ ȰhB{{W"hz\6xu};Aw#oZ&@$|h2Iy  LswJT*zMVG e>@ 1@% !)ߌ3,s," iz H XgB]Txj,#zgfd\FfOIAbBA >X ^@JR P 1ïFHlE^B@iA$H,(BF_*Jݯ\Xc=%1AF^H(5$& RmP  1@(,ߗA`1jDfF+1A$K5f3E^/Rc}}{j$NAb0  QqD(Q f.4J(WD$O7Jxa(`nhzD #ăڲ PXSKdO4`,S)j<4=P Qo< JW)W}e%#x# eoC!#_\=@oO#ZAABPO v)Q#_p)A8A x1"5 `Z(Uy儂XؤɬX $%%!,,BAbhY-232tBJо K *V9"'>/l\jD=S+ع3 O]۶ Eee9l9X~{ %ˊo3l+"b >.%2'"Εa8P(5-MFu+ d8BYꖪW<zpRwЭ[36?v=f,$EV8涜.=GH=R+py)j3&WT(]!@ܙ]oD=l6 0]ވ]v+ݻ"&:<99]]p%$& YT`tܹrF\FJr}&bc &)) rsa/ݎ<$$$5f_h|?Á~+O 4ݎ=K,Bii)Μ9#G2E Z%Ɛu+e wujua1zlzq  Cllqطo[*w1u:u?3[b]ѱS'<<ii#2* F=sg 9@ 3J'l4_\zl7'@ o8cp8~pG;#[7Xh -Z"zNY+=S&x<44k֬Ú5TE j\>Դ4S;v.K^'QL W,U}aqع3 7nD6m*#.Ǝ'phb~Sqz%j]jT1'{4-V 9 @:=N*4Fzx Shr<J?;qogI>98jE`` ϣ ՈA^}вeK@6W& ۘsW@IdFtt;b'3p7VaaaqPZ<RYvdm\)܈Հ{ߗ J*=V @#k8)Hj#v>p7rkմILh 6;eAz@~,qāe5BuէG=}M(  -bPݭtgB鎽2S;'/&@XroBl@ <  =%yY6Q+XGJB-ۓzxgYSK@ ӶR{l2b2zL]xXu&: ˈuU{PQB@ɪ9FfFkvZ*[?2326Rm ) b-+_.f=,y ŖP;:bsϢF <1 ZgfdHfc5lr#Z S$kYNUS-bBޟCs' գbQXGB '@G@$c5Ej%A#g][2'f厳… e`G%[/{s3  %tY K ʨ%N@G@@%b 1@H2QF,e.c1RJϻ|%{?HN XUc nYI{!5L#ޫ>XV`$0" 1Pc3eؼETb1,T^%|+XlF26CUe9ly۶}Kj`Z`ZDL8&!.6 C`۶O%VV67"6&/_n7/oA ,FAɲ:-#c96w@C gw-T#$fRFJ,M!x/ewp-fLr Fӧ#1Zl~m\xQEbB";̛ܼz vHf͚8P0:NĀj0,x $CZ"x'}&&Ĵ,%,2&.;wfkѿ fÀеص 4٘9sbbbnom{ 6&ң =֘1}jjjQ툏FtTG7. cHnx3EUUG{^y%1A9Lb0+rw0BZ)94!xBJuLZ@II1 z@~Pt$EڭVDAA>b u\h=&ncYXi@Qq)l6Nԣon߁3GttKy8z(99JNH5(:u CvAcժg<ڳw|Cœ9$sHv=WN^lW@!#$u/K{c6L#Jr?ɢB @rr2~@_|1b8ڷkɓƩS<6 K,C|V:{a۱hbƺp-"44aa;wjMy K!) """\'サ#>>qqqXl|ǹ|z%)S#GCH` в:,糌x6Q,Qjō#<) Auu$Yǡ[ɓO>uY)v;`\ RlӦ Μ)f!..u 9җ.ANN.^w8gϜA[= E:;wΣ8{ǹ#1@2byj昕zfr]g(5y2ȝCB AA(/;#+ Qfj">>O{ :8!!!򲫌naa!ڷo8q% BOxp<,\W*ZhJtG;"""PXX:Ȗ~XXUmtV: !@Zojn5PR͂Vf5rde^(/b+I'q@H2b8[<4a<4=zDuu5^y%׋x IDATt|7v /lĔ)PYY *f`ႧvݳZX0I?Bhh(бcGQ˗kѴi3˗{3n?VgEHhVZeV01j,]b`'qȑiP̀KW}z 6ᑛߘ̌ ݄hs?BK=PKXXbc㐝 DcۇV&ZؿêϠcճ;uС}2 ^z_ݧnf-[Ĝ9s]Bai6tGH|UjX6 =#ѳW/3gFv1x٣HK{QQ߯7H̝;< QԒ@yz9=rNxEH\1W/@OKxB`q1u8ؿ?~8#Ǝ17waD{&Mv̕iYlHM9_#ƈw{Ó\ Oc5ka͚u(} t1H\kJ]r\l"JRqކ 6,=BbH&/AAA;vvƍѦM oc!((: 1#~%(W]`[`IòFx(V'$6FBgƽ@ >ϟGAA>e˖{p ?jmoYBkKz?z:seQʻB?nFj",, ݺuǀѭ[w H K+W=eb@D0tZ=+j=j =A$+AвYY܋(osRA$  1jI0*t¬B@(AhYڈBAby_(YAf=&6?: 7*Wْ}EY,ˠR!}FL{IQ!/z{pelW]];pt ڵmPTVc˖ 00P8Wv^Z$&‰"XVleRz$ʙ"Cn$)4.fXd#bP:/#Z=WNwЭ[38vdggc;oc L5k js0n(dnfr/KMH}Z mfFh&V$-l8_N ;е߿?VY`͆8.X,rvƍ8y;_uE.]ݎh@2(YMą 0t؝Xz-B[p/N,ęؾSX 3ga [^UUV|y1{v*xL}qѮ_8,‡[90r=Xi?BшR@EIf'b^FQʀ+M$%ᤢ%%3z, x^8&_~X~=.lFػg>!cΜT|N">.' Ghh ^}eXY&ENa}Z1QZ ,Iਬkrm.4b} YG,Ǎ^bO SQkI?ɒBiܒob3`ϧW"&&SL5^ÛomBbb, |jev;-ZXi!!!] 6-}$%bAzr{n{xX,,[G,` z{&ČCoR2:#^IBw_!Q3@q7.6󒯐TWW fCddh)-=!>.1P\TQf\\4ūbHޭ >tKȕw$&%!,~bsܹsHr699gϞa}?]=?k!03^rW GB`TUU QYY%@`9ϝd8“8yEťA{Bu&~8r+Wf`޼820kO8('1@:gI2gyOݮ#`R@ :WK:bK9h|$%%Xn.vCBB"g@PyCQPPŌ$˜1}K\ƍB?_l{F%PZZ3g`'qȑLObPe(5OnV!4"AP{cuApcc㐝 D}ߪt L: ÆAtS>}aQHLh)AJ2ovL$%ҥ~GfΚc[ѳG7KϽzOܹ3@2G/F 6# KZF֨)CGg>v޽pzB(+ѱsYfCj\s59k6}{N]?{dcƌ޽(*.=2dra/8lb͚u+߰  aa?Z],YYă!`q`j!0kD cǡt)6n܈[b׮]غlܸ%K1v8xqBb fBH ffd03FMi[`h8rAj\{`` 1cXpQ15/>| @}enŰ2b3;)I٬f#tPM"1?nV+Э[w 0ݺuGxxUY Ā ?J3b 鋕%==E6'@Q}.]<ɥ-6%tRFG fe}$p) &VY^FN=Qc兯@AbcͺcRc6-[CVB_O2Ah@ȭWBXkdTAz|9{ԬR!$Ax*# ,hW@f'p%54y 1Pz]/T(@(Vޣb_yVed 1Ѐ=Ns+1B%@ljAH|P:K9jLHAb Ko!`(ןj6p"M^Sr,hB~x. F3Pj'VkgKA$t2*,n^,b@A 1'bcN6оf'ڶk_{:P믽ɞg$*r4RXIe̺232*QڥjTo| 8NJD!1`cto%b#,͆ӈ{JkV]Ba6v(]&I `a'4hc4(*4/DQj7iQ >@ԒB%bFC!qR#<̪K9zD9AQކI,@խ!ш52@ψQHQ H Q;y單%%Hu|.fm0ˆj<%oEkCѾq meeeGz؈z&>!^/6ڤZ{5?tFyk]r}(iPM>Rg$mbH%g5 Bc;v̌ y9o۹݉-T3 X3RRx6jy]fFhX;i11mCc6m;A8ٰyF^n.48p3z?iB?B(2]n:HWKXDYuygR^RbĀۆXH y [@FڍQj$*HЫ n4+PJuOFj(L}2I _l ]漽,Ri2о؏>ِ ]pղE0jk/ᰣ2:8vyꕘ`1$RiڕQ fO_Haq_0A$IDLt$ [2][Ss Qt/I8?"ЧO/|'ιb,,Z`XXxILZVpłxR? 8#0N“ŚV}v;4iC{Ԏujֽ.c+ wEmD/#fСo0㱙F``  ouŋXu֭bp|xGvALthϮ_Դ9tM  A7!mܳb`sCRⵘ1}]Z1yTVVbܩ=$yl~m{(3ᄏj_%d{3k &K,餽Ejkk fj绌ju߸9>>'4 Ċ< aِ_P(9r?p ~;,S_{߇OL)V|un3y]?=O}_BEt>Cwh=۶} lrC(9ר]}0x: >(W23.tSL©S']󟷰`B$$\-B1o<|<&Ml`E@UJ.haѢňCtL4/^-[spflfisxw;Xx ŋ0b>Gq?O|8AmodJb+c󖘙_3U!b1ud" : ׶BVqֵ 㽲2p'$$KLL™3gj())(`ios;zf8& #ph= ݛV1C ;?yuxo]{^/P[! O19_=zė_|1sн{wr ]8qQQQȵ)..Σ,oc*ֳgbח_"u,\:c1}4|s'JKK)xҨ߷Mh\k|kH4B3ؿÚՙ0p`?|u2ez=0ڷGnwIy]H6^3У'/'/^Ν:yO`ɒtcޣgO  E ̜*8kcD۶mqw`@en{k ͆F߫6z7z* (+E.GWE{úhwqrm͚5ŴibڴG:`Ō30c k2N IAʐzN*Lxh"yH`̌ U!T5@$ cxI$q7k٣\#ͨ=fm%fF F/ e$NG;]~̬gsP'^E}ltsa܃ߌ4,b5;S=zpKK/z(Oq/aFff@X=3~ ?E/5g`#ec-: a!#g`%m[[~ߧ /&9Ā#Nj摌Ȉ{JvZ M0Њ bW iYO2KJ$iiAzV= $%όX,8pDA.]zϟ5FZ-;ʕkFcg=5YYhbZ+T54 A$0 jZ ?*fll t " Ā{FINCk֜C7+GSLAF@K 7eșkh%ڮg>Ab0x)+YX6*rc H 4DX;`0zK^5GfnEk 99-3?bQmnԦ=FTX5AOѮ][@c6zG`7*NH~\Ͻ~pVWT'G㑨ŖǟhZAɮ.0bv%F^mr'@8L8&!.6 C`۶OgS/gcc"qevslY:^z_(9}o ~~&02>E#I`ISLFbB";̛ܼz S! jf_~d!z&MMObj뽯Z05}3g!&&AAA馛֦WWWcYRtc0q\8^,sp|tmuhvE#:*\hաuX9ǎ?kQEqql},mݾS qQ?zC]Bmp+o,??3bŲ?~Vs׮YGbgCߡt Dal6NTN_u΄Py IDAT'<?;wFŲuc1klOȏGwvkUϠ)޳Q\\Uj+KߑhN=2GZQR>K kyJ h ~ucĈh߮ &OzNr[Xx вe|j>q)8w6ːHO_hڴ)f_-܊Pcy]if(9}eHHHDFj:On#>>qqqXl| UT[YWj\o@jZv̌ by/wbש?9q.[dλ_ڨ|o]IMKRLreH}1}qqXf8Cii)6<'=| (*:byp'.g9II.4vB 9cŊeŋW\,[gׯ54G>bm?}4NNNٳgDE>;SĀsc-Ę%oKMKsݏ ƜzYY>O{ : }[_$,΍Gaa!:t˽O0|)WѢE TUUcvLɵWx7< NaB"&6h߾=ĉ=_>%_yqg Ry1oجv(YGǽePJ8ABkv?ٍ;w7<ΝsQPPŌ˒;wԨј?N ̟Xhh(6m`ቹ{kIدʳՉ.jȻGbE(--ř3g`{HIa#V\[i匜# 8H@Ak V>ڢW8|;l|E3fo~;fZaG2D,sS}{"22sڪ!''qN%˲P?֑^#b6U^ >9ɕ#}ʰ0 z絢./QQ }6ר]تٌChy uyFZFjM_̄z'Xn>1'G!Ah9!eČ0rzNW1h + )=]$VImg99[^j.F̨3aaYWGkzH_uN$+R#vxjQFNܓi3vx$g Ab.*'X 0s>]`!sf2f0zEͶ @Y%t>5gF8h^Fb) T3@7@H <~A+1B#j5z,Skh=゙^ =?/+@!!pBqAaj\ElAsZs<wuS0Bo*uoA#Pk Nqp}L 1Bdz[)gH^q c @Ch Zڦ8Ph C%/F#U\2B>Rg$m\3?3|JraLK.#&qT=HI#`VG7h ܿrôiSd Ɇ σy 8@}.doçt!rk%x= P唄yh #rGI03?6Ɋ 6Bƀ%ZŘju85UG Ea5jWY&+@I4Xz\,_l;h =2roZ{yi8W1^1*} S_}~+G]]bbcлw_tЁUBEh8 ݎkVcTX,XhGQ* I 4`A eX gflڥ$g@a:/0~VeLk>A֭1iBaIٳ]uuvݍIX>G ZHE+Ig+f=P.׿|@mϑ Fy0Y  U+Wbvj*V=k׬+Lr޽{PVV 7vՊ:dߏ#G~@mm-ڶm!w Af_TT k2,s/5k˗y^˗/c߾aѯ_33q_HMaח_A&MгgO%  )$գCZ~ΤMjb(V[a#2%/!<8{))CХ]vk%%%l?ݎٸݢ{nt -Z\PuOesoܹqY}jj$*\m+@PT]ƀD< 2$R332<%EV=2adK&+KBˬ͝DK3瑞䪘)C<5?|=`ছnFv`XPYY߮yxq<5kʪWo˵سg/>|F y^܋/"..UUp8ʪ8NʋA*6$Y#THZV7x =aW"Q<}3pl{s撇񉍍C֭||Թ ƤIe˖W#&.8C[8pk FYyx=788.\[D ]?t1@!P,W`=YYi&+Imlh .-t]gxDً;ҥZ.qڊ#88A c`[?ݺwGLL,uu[&8 C޽ТE**^?C"X?;:Xի=tq !!A]_Dmm->dݧߋW_7l6v|S=@؈!0:MP=juP B -NπgYgLL4/k&Mp>;v@7"$$6 uv;**.9Xq 7_ ݎʪ*{KNn 4mP[{eLM1.sm6틔!JPeEX,@^=qۭ $$JdkĆo#͛_qM\!1N$7 K UOMVRTեes$-)<(HG+yN*|8]z=5X]#Z*\bjCM%y߃Թ<ϣ₠Be%.TV*yW!1ؼb`cNRU%XU뙩QIYBŒ!@l': @#bAuZ#f,RD?,mC0s狸Qw[<)h Z5"Ĉ-2= Мcz 1@(9ֲAu(3j IJ@xeIB~$1@^+=8OHhW}-d%e332<2jB)ZW <5LB:qx 3!Ad@ ^ ^jH` @+ł%##$f^^)BI :Ǵ$kv ѽ>C4< [?`H=w qͷv#9K$%A7I 99o.\@KmG}rU7oP!X_~]t QᨭE]]!>o^O[@@@, V+._:~ 6  BJJ ~'߶m `DDDGQQK4>|8BBBкuk+ C bE+xfe[o_2ڞϿ@H/`VJ|>'1`qxKL^|M6u?~{g<>x'Oj|ֽ;ڵkK_J@ҥܩZbCPQQq<"[C7"*2m0hFǿ^yuuv33q]Nh*SLAee…J,X0:G_#>.vq1ppm+3:T [,#&b\pEpM41\߽qy0QA%HDn7U@e%*" 0 ,UujN>ݳ0|?ZU]S3|g#Q[[$Ǝ=ÜlM6F"tV\)S}M7݄UVn+ϟ3<%%%ׯn,Y$}y睸qyG2dyv!??Bmm 7⠑ñe⠑1㏱m۶Ӎ='GT۪w+@]q](ݏ>/o^{5j8%|gl27C-{>43jJXP)ueH$2/>Fou.]q8,YQGgoG^%7 N`C׮]qF2 _4if͚jTWWc̙8SbEEEw ,رc-ZJ :=z\m۶g |t8?~C_|f4x۞:B vt/n!M7M_ S !V}qq1***PUU_hn}>^p>ngy =_{?gàA~V^Kv.>v{>3 7UVa^TYy/@k.$ xlقp}SN ‚ b]%?#x㍘7o^ܦM˗/뺘:u**<3Fظ袋;,O=c=>\t%o} g@JM["'b-:u1c'O>~W /Ć P^^'c-Z?=<֭f̘}}3믷f 2Gytytyh.&x"N=d= ʕ+kYp.\ǎ`!)`(\}GvzG)%-\SN}ee0l0ukѻOz¼^_^PVÇn x^ %%=0~7O 'L@II2>? cU^Q~}KQ^Qi} XH~ѽ{wM7݄m?O2e }Y;6[p\ 6[wu1/<8;o):,$I?1c4{$^h`[n_/zj\{5Z,3g&/@*d>TTlBee%6ugEwq ۶Uo ___9?gy?!>]@(7l½_O?T _}.2s9#G̙3uVTWWȑ#&~ٳ1uT831uTTUUӦMĉ>zܹ . 㭷⭷O?ya .q IDAT3/֬YS xX@H {Qb+2͑'+ _5  {<Ltv؁'k _+G3v?j̘q8}"<~?g9gaΝzmYy7Rc trb583q%`ʕ(--Ź瞋_O<n̜9RJuQxcSL8#"Bb())OSCݻqꩧbܹȧ_߾зoO=+R?|t?ߡt_@֎uMוml+x˯Ñd3翈s^>y.x%O<0ރTUU'%vi;v4mǕW\;g}6{983qS,?)BOBR@K/# BtiF'z OP[[I&aڵܩSjn x.aL Z l@pU\M0h`̙3^{ ֮]5Ea I\5ki#)}.TD!sm>yh.*+7ag}bls%Iq:8!_DK#F#r6x<رc'B3ESE 0dPDҮqkMͫ@ I Yh]:wFSᐡC1nxDΜ`o INk# x O'AwQC8O 'f4jr xEl ]{իy"?#ŋW_~*ǣ5c!B)X<:xx!J, $,:`Bi&+WPa![nK! VBH'y!\5rTe!3@!:t0zb)0gBZB0&zmB!:J* !ᲀ  !ttgPMB!cCgBZ BW#!?\:ұa! S@!tl BH+BRj$BBgB:6, $ѝB6y ! Bi%X@H ^ZpY@H BHdžbB:3BR&O!ұ3@! I«BZ.  BذP BHGwX@H ) B:6t!`!)Tx5BHke!3@!B I^<BHdž!, $ FBi, $t!cBB1@!`!)kB VPH!i j񓧟ΓBHb4YPNdSPB踮* }‚b4!Q9 !,(ٻ8B`!лW ihP ()\X@H(HNlV Z p / hC P  it?p $WRFYCq@!baiA@JI#PRP &Ud  , $i?">N D.qmKPҪP fzpz`r 0Aq@ȞwX@H(Hs]}!pQ@SRB)PW@ƌ0<Ł)$%)))X@H(H\A݀Mjխqݴ`ق9!̓b+L3Wj)!B y(rx ljsB  iQW!p.L#8as"k8 ulWET)߇)!c U H-X -h; ѰAx?x<8s9ĆyzXQ %c՘]X9y2/(f| B1@z Ք1 6 )B`݋ubMmVsd@ v- ܎%5q@H[ۇa k!v\|@t KK }qqBq )ZsR5JRh/ŮqmASA]/5 %\A5vShD*pR9B(Z] t dA5 !DB5'cAy [_{(is!hBAANW A<fA zX‐;vF.pL\Iϋ,s,U Q Ap]x~ B!mv $\rhpvrg CA 4bIdi76BG޷rd4g/TR  @qlz~uu/qPLA=(g!a=}Л4&tgBjo>}M %%߫n+g Z \[Re3Җ`!hG :8Y8xףG 2xQlۖ hEBA0,sP9RJm 8HҖ`!h(1="i8/h gkgk ۬E"JAZؙZV ‚l\sl1EEX@H(:`P" F@srE ln4 K)q% ARmO )Ji  @w8p^ā8g6gHl]Hz|_뢦ħlE>RA@m(h(!s0Sg@JPRUWGk00ʌ2 Գ'Ψ+zoaP/N$A=W! ϫ]Pyp|fS  @; ](!~31@ZWUKާO6nlXHB(@  %\Rʆԃ)\ ƈ /6a!)TK9}j^+N/13 h+ 1ܳg(қe,-|fƍa݀ "+"\h;B H8Rܣ<A7~6"{Flݺ/y>.Kc~X@H1@ 3g Wy=zu+s*E f ND_?q#_0(/O;ZQxR_w]tJ$PJer@i  @lhH ΩPBmŃgn݊{*'t ߰!NNМHa/l9yB( 0ۈV'0?ѐHں5t_A-[2pby96ŃoI,/ٓ**j 턂 hNTy/ !iOP tw 21} %%a Wڶ /[̭[!+zaҖ-xwS++C*Ы<<+*oȾN!Q`w2Df#4G曼HbX`DAm"3 nl 0O0nzeDfmP^k &SQ:j0$%i'!h4'0AdH8Ss N«{c-aP=2U @Zh@|HtmKh|Bڅ3BB1@Lں/"3"zMxo_[VSZІ9@{c`iA#M!bݸ &z*@_7okǖi(o؀w7;\E'WT 8eGǓ8 B1N,dWzZ3iYzS++1q#@zf{ 4+wHxgoۆڠ9FW8q++:N4`rd6"-Ռnl-"<"]H8p ts")ztwq uN,/ǂ~pRyytJ$& 8q3W/xOJԥR]_!b0Zȶ[¿D~@$5;F 8PT#y _:wdfSJyEH~(:.IN&M!b@D{N.Xh nu#i4}Niщ呔A`ꡔA`h8mf 8n Pl;hQTtI&nA2 B( ^tD#Eq#yB`AǔRز2,8MENظ 5`A2Z#>~zDM@H&DDX1y2f!P + Zʙ^n ńR8H!hEPJ.BM2 ]PB@r/.ʼnv B±օ B,1\?辔زk6w6mBQLbё p@BNpBTN7~{f|GB@ lS"d442gXZSV¿?gPqRE7mJ Qk׆jԨ| ")NKKpon\nB1@ h ИY)( Ǯ )DzM[y]qyÇG^m|@ [*|Q1fL^~'t-* yk!b+k|]PX}{\;'V\C2>}/#Q;`M#.0k8B(h vbqtB[vPnt{S aoF !qZ?"~ |)wE󰽦&, $P & at'&dzlPлƉ3U`u3SǪ u MgBB3+  il/cplbb̌_S!ұ?G̀4D04DCHPq谤 X9$w? AB(: O~:*L pI5Zhx(,zqr9_VNO-":S(Ѕr X}l46r{S"e.uAB@ ?Ľ#ZH"Pn*LuW|s(hpѴ&zׄDĭ!)p䓰0y?sеk".@!. _;a}oε,q 9i6p?W\~ Wj LEds ̺O-Y:Cm*'Q?"BBslfѢk8h"@ݷ , Vzk{o:(Qࠡ0CN|?]8p_y;wb,"$P 8 'dǜ|B# I{L􆍆`oʅ–7VzkGIșD @wLQ=Vn L8޹5B1@A P~#Z3.yq i y鴄E3-zd \!j͕3:bV[d!!=4^49gѿ3F[GF@ZD@^/7kliЉ xП7E\d?~[zzpe]wJwBgA{+Ǎ Gr P3`jY_Ӡc qML!À ҈W: d{"UBv"$鍳B1@ P,ˏ;.KLepdlKV* `/). }ZB"p\ǁ+6:va3!!b PB cm]\l`@ As tA`k8&   t.EEH8\ AuQ8YWD9c#6V@4@dZ4}2fz#0 E?nun-^O3@ !ط{w߳'ul_JxMMFl:!:5y FD31BRANh1$hkZroIDAT[o !('#`OYʋ6w 8Gfz6C!:d:f{Ǎv@,;[`0kz Ӛk< P n%gT3(n8!oӚXV`h9aR !b .v)3Fș*ȱXQ5ʷ׶jlL !HҾZڙuRP@jytat3si@|@Ԁ-}Fi]Q&`i S & P .15ZˍQrf >K@39.:߹R^v gQx[Kތ ..@ƱImrql)My/g,JDgBguw@9 q lk[ F" |ԥI3FBPX3@ !o؂m |c\8'$.p#OW@,h? W&(AD蒇Gm&A8-z~ RYYe?6W@~#S.A.qDL u#` 06Ź<QЬȒ@xZf2W@JÀ)BiuqM,֏*tAaqlARx,>ǎJH>k7t_3jB(^R"BiX+"O"ꟲ|ت+@M-,u 7ĊZة6dB1@ OlJmXߡ 9\l:i z ضsf퀯PsKgA2Lw@F@DdߘqwH>_c|OS` sI OgBg̝mӦŻ*XAGiC|#ji,_~ VQ>R/pWB1@ T2Fѵ&ZdQ` f.  d:D $=z n!4Aœ2=zir"2jϖ뷾=sWӐ8,fF-2s@ n?!)XW |՟@ʈH?!hȖ&X' FM$ Ϋr(! h!>|׍c2R@F@c.@@C!e"W@_ؑ}srRgABH{i6ʮPx̉-gq lv\ A_H#&t`;~` t/e!P fag&'-#8 @ oAl Ĥ@z" 2 +0xB: Lajѭ8 j~Lq\+ Dv@J8ق\؉0w@},;J(!Py!tH0UbB="qlyl,QB zr_BIhz7!m0ꡇPL64qN 3Esk彥.,г 4vH ~\J+1c( Pd!4A[wjkQҥKX;ilj"0fKxPRB쁐P\MUHj{6kjPJ>rf ye\E1Rϖ~rlq =;n@y A@3@ڗ;Щ!8ZcqDR}ȠPuTN0 Uq8 ,l &"m- 8b! 탃z(xR)x0^_`s%0eYmPB @ XB(HϚuCW 8H9'H"lT QHk e<ԫB  !bC3ЫkW%=7D@Dmp qJ(dq558`,㯕B(H4s&jԦR( ` TP R 5WB/ԧ !^[„B1@f+Įztn(r]9RB jB`jB@KFb? T յ^S>Bi>h#7܀;vK5z @fTAd}M|ZU !bToD Q 1A$`R ;GΙ}+":Θ2uU $\7k@}_.Ruu^S1?B;_\uJtI(A[ dUO YW|x !b#|0/Gi(pަh(Ԋ}]@ B1@c,|믿>L?=!P v΀I 6S@!P B!bB!B B!B(!B1@!B!P B!bB!B B!B(!B1@!B!P B!bB!B B!B(!B1@!B!ME 4YdIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_bool_intersect.png000066400000000000000000000662261466047437300256750ustar00rootroot00000000000000PNG  IHDRWbKGD pHYs  tIME2& IDATxwxTUǿ3Z*t&TYXV]4E^PPt*(DV )&$K J23 sԔ~>ޓy=9GnġD`Pl >x+z1@^^mێo? _݋?twB?RWĶmS~DN`۶mV|Ll,}!nT*s!D4{q'TŌ :̆p8ɞcGq)%%E=P B!m$&:!4$޽{P]]]6:֗F!n0+9{ f͜u7w>1) A޽Ħ͛p 4|<շ8L@q;{O> } Kl3_7サ%%%xQx}U<|||eeeXo߆R 6k֬E˖Z/ŻnAFU^+ŋdB?GPUUbЪU+C@7\TT ^×_|F=8 KΕMq;vºн{M 0gcWE^NGfI{hp{/֬^_N;cEkWѣ_-[k+_p%K!((wu,\o֬Ykװi;t 耱X5?w׬ZƴiϣsXh#Ew6#!8bB( ,4kYMM ڴCFf}<:tJ/b臑@aGq뭷AR!++ ݃3gO Zncׯ_GӦM /--E޷ b #3d_bt]_lpƌ~vp."$$* >W334*bzmH;(Ǹh%5uX חF(㎎Fnn|NN6* @ףp5Μ K/BRRJ "zN<bbbpZɵ7%E2o͹( Ӱ!$VjC=1ncw kt:Ѯ];˗W= {Cm,a<#O?~~~(--AmE7\dS``dR8Z%'(!Lծ7&%՛2\R!ӧsZn>}e˖`˖Lzٯֿ Zo=d8?a3xyl,]QQQHNNƆki;=hڴeK@EJJ*3-5R0\<8jkI[)!sIӧO+>۱jU<:))Ɉ+ѧO_^v>}p߽w#Fk=7B1@iBn݊ǛDN<7JAOA@(!WX[n>}o} ! 7?!bB!Gl}sO 4 Aܓ'Obĉ'NH!@! k˫5`#P;ˀB1@i@r,9} BV !u)$rĄP BH B!D!__ P BdcR犄<خ2aG!4tt1A!sl !pj!!h*\Z};(i3 {AJ2(//j饈kĀ+m{rcҾiuݵB& ;; mcc|u !!,_{gpD`B275َOѽ{Wč毯dN'N`ǧ`8xyyk4ar?!BHRWL@n]пFЭ[>=A-GAјJt:oo߾1`@_m_z2,^ǢUx&Lx|(!~ $ }BRAׯ2^AAAdd@sK"Y3_Œf"9% {~ge$?~^^Xb9_<BBKR#*jK #8p~.<<|DDD">~ nǂ-|z/_F !(//7H%#rrn׭֡yXtFx* 99<^X/bBFA~RIF, @EE|||&v?ApҌ>~xxx? {Cmˢ Gcإ0** N%555HIIA=%#ҥ+~{&M̟*zS /"&66 sfcBrr26_M a!Za8qzhުDbz`!ի{c<"[cذa!pѷo?čȈV4Y <8F!8A`ǎOѷo_~MM ۙgZ9b ~{O|wg=7رq;6͛7__/bBB\8>>())EZZ*[!.nCP B܊ƴ^Z'F"A!٫ZliG!٘${MCؿc'Vj1 )tt1A!slRؘq"B(!B1@!9ưl1!B!bB!ҀP!7XLr%M\8Q'RB1@Zq  bԡw A@ LXc!P  Z+cܵ5E^̹7.g B ]Jk͘9Sq+RiL Jeaa!RSSQ^^OO/DEEAN(sDq[iHYG?HH0[V0p/6*W$$FB& ;; mcc|u !!,_닌h4_aWd|(Hc9Q,--WR%qa!>Q^^v|ݻ"ngp ////rp؊R@'\tkr%Eyt’-A9x0ݺuAor @n]pANeޭ Bpםq&QZ 1}4DEAddkL:Ŋ"1.wgD-صKB\Iaa!2ѷo_T*pC+((( ȡhx8HN#GbΜY]v 23q 8_dfea7l*4 8L@ER/o|{D]Kj "孎 D. 6& @LL 4tXrBBBR0i[b;?ہDxx8T*.]1ƢEKE(H kZ=`i|hT^d3cLzAyy9@R E7iz䳚j|``!q yyy2\krE(DyW,_^$<9kln);">mJQBJJJ$z>00iiih߾T*jHOOGv/_FQJ"b@XVgKBSTؐmFhm2=f3hpT"t:MM RRRнGOk=ò++W1\<#{cbXfT*ϛGR\ix0xJ8SKNa5uK6MZ/܄V08qzb 8~8[\hƌYة(n@1{6EfΜ }zD`` yFH] rɅ+ [(V|Xa ;>^G߾}Mz555HLLog".njx{{cEX`52^[/֮]kTib"!dyzu|<`g:'Ivѽ0cL53fΔڸ2Vꔛ v?mD]8߈{zclڴ AII)R qq$"!bKwB>K>d_5r[8(˼\ȁqۼls;m!5hag/5 c7R9bєqm1zj1b$rg>hٲɌB&uT/Ֆ%ʶ&/_MìWfbv6$4 +CY;yZ VˍHݽl'X:),ݡskg&B#ĩB@bے|go oMRBǞb2J@ .RfR-ցҡ G-K9(!ԁw_d_2lqƘeH cۖZlIIYIE-vC$F{J6W,8:*`:ED#Q`ӡ5ɋB(ÅxX$u0F)!,m=j#BYΉJv\$s\(̧Y@ީ-Ojۚ޻b{(O?[3^}PR;\BuzN)e\79e|XIReZ3$lۨH*!&,%nRB(C{bݚ{J\Zwn3[[V:ta@1okWBH}9Nb=gֱh %;J{v)gDȵ5\,!vsd!gi򽶔/+X6b`OFI#"Ȕ8 R=uKJtZ-׀-'{V,R'!bBfο#[ ݛOSꜭ0^tȖ͇VQ5byc;GB([Л(f.:8JtYuA%Y JE!bͣRҚ#ƼD :CXhemnNNN1$J8}4;ӧOek}QYY NgrbPq-RK*]c%nEFqJ".J;^QTrٳ;w~EdDL:> n9ӦNEdDI´FSHNIܹ!7on"X9|I(J3< ! [3N G'aQl"FL@n]пFЭ[>M Ɯ"#Z!,4yW^5SRRy}DGE`uu~.**`cqq=[ݺ $8w9O̗bQכ'Y3RVVHh! &1RAˉ%BRXXL* t@~qdAݱfj1a- G'opU+9h42IO@fV.> 9]@Νlb=z.\ǟN#;'[c֮]L;~'NYYX {Ǘɩx`H̙3/@ {3y6'ҥ{nlto{іȏ#ĘKR#*jK-wEAAFvm15 -ZVK]nXDt:,Xw}|}}cΜWpa=?K/ETT4d2:j,\ ҥ矛\b*DDD&M3g);Q$( g'?큭^IWy/k8j=b%93G#`ɩK$X^lNGhsD Za8qz8~8[V-+qxc>z/Ʀο [oÆW79}{Bۻe˖5kA(L<Ç 1#~ /[xleeeرzKgפw[SSDv,ˋ8]d\ņ5_oVr!|M6ɝᒣbNI@L)؉Qblڴ AII)RފB9)kQ:NWԹY#6/vIJSutb#̣JlLV#Fii(//GPpzꃖ-[2 q&|˜ lq&I3_P %X#^\q举5W%?JEW`j5Z-w{ {7f-scyJ*V{tFg8:{ʼn[ژB1C=9 \Xɜ{Ke;6n, {w2tx!bG 왧ly%9R3 KךwFB>!bom\j[EWMJfeP,B(H$( [] ajȄ%aRbB1غu֮&(u҈ppԌ [% !bԩȰu3!kbC:骱G;w{ gB(ԉӻW,m#䰜R[=N]pP"!>G۷:>HH0q*Ha|nu|>HɆ3g*;0ߘMznE6%%a#uM +˵TyBI ;Ksm!s.ko6%C `ͳvkR&&'B("!`' O-Gl-BVz]b2!!b#=QK[J &Rd#Gf#7_!P 4YzYsńCgE\Xe (!n,YP:<`k^=\p(`$B1@\u>J)WUQڒTɤBb &/,,ӧqQ>}l$24l|GK 2h0g (i?g3cLsm{ڋ4^T*& ;; mcc|u !!,_b:+++&3h/ f#.Z 'JEΈԧwDr'!J(//g;>E]7vst8qv| ƌB޼ysMHٗϗb V-圽TRT(^;*ڡ40q=EiR*D" cL@n]п8p˖-ùsСC̝;Ç^0Dt&QMq;vºн{C!<,ɖauؾ}JKK1tpY-[j e.^ᄏW{}e˖ %9!xatOI&啔W`ǧZ_| ^.j_xX碢B,X 0QXl|}}eP #TҳJl&aQy+]Ι3-Uo@ܛBdeebؿ?yTWWC:u ?0v܉C7DAAe#Ǐ_A;lƜ9pAdd <,e˖I;l͟+co%|?AAAY3_’%0d0]ᡨo .`5knLϘk +3ǎZƴic7h'SΦ$ KNhMU=vk7JrDڒ3`N[:34<.]JAlL T*/_nš,_Æ CLL 4Q1`tXrBBBR0i[&`l?x}lh;"#R04tX`!BCC QPHǯZVT?g;v!*** K,v~;w}pT*,]cF?lJ gk5rG練2.QXx%GK:22@)//7ܹs7.C\Wg 48moooTWWt199<^#4$]ň 2M)nǏaУ{WʻȨş"B>etoLL ]UbN튕^#6S<"T(֌31n7[DZQZZ Aо}{ڵ ())L 4 (8__ĕYAvN}]vlogaժxzE ==]}ƟkZ{/_ EO(\W䯒޸ϔۭ qb-Kmmru-bH"** $'CaΜ9/ ^DDD* H]닔'Lx/ϙ4@rr2M"Y洩Sŋ3+oܸ1o+B̛7W>Gŋ ''?o.5Jz'F΀"RN͑DFGwvq_Z-BCp :;v@Ϟ==zO?ߏDjVS0|0y۷ƎFdD+L,"Y=އg1QxBlذѐ@(W_B0h=y{wKg\̙zO Ĝ902`'PNJY0NFvk{rЎWi$E1fР!رz 4C PTǎog".nB& XZvZF0c,͛7__-c0vl²+lʛ"3k׮ڵu p ԐĜ#47H典q͛o>=z}͛7#+;qq$=G\'7%# \?ߕN͑u+qV̤R;#Fblc ^P!(8<#FR*䦙W3`i&ώ ]r?{egkm NWVE=0`@t&B1Ѐv|2 YpvdC> Bv5'l+d7 {r]B={TT@f9rr}pSwػk%B@NJ\[U ]X!P PH:-[{?'ZQpebX) P ܖdAWDߕ#V1Tଝ !b:|Dn?B:6Wl3A!KK9%!}vȳ5Gu%4= E!bD#zQKWr㪄Ί@X!7܉۲gƻںԮ\(rWEWB@HW2< P(ћϑg #^Ķmʆ uoEl۶q c;,o?l~XnxJ..K?k14.eLH]wbD(0BHHq:c^W-몺8%8R("&0sJVvWPrUBgo{l 79K-YUC:SB1&@1-vHt5P#tPd뗛(!j'@Rc>BqL K.ǖOA@hXZzXlL|ʠܽJz h1xMNP6Yg#P/ 18RΈER@9>sb7x0r 7\n7kWq+벥=qij޷:>^4B̎7 {Yؑx)S&~,6n AdjiGm9[k}R~Q W}sv]4cLzﶼ^-o#/RsPUG"uƸ۷ˊ&:KsRvT^l}&{Euy(b NCj/HAC[)К;cWDGs@u* Ķ#veo({Jv$GLsj i/DRF hǭކk 55Dj-פ$~wxzyaih4HMKGLIq}AR4h0N:e⼎'ǾoEbIU[H$&.\}S?!;'*{+++/#c0z|^TTT08jktgW,^8@I6{g˻((F=bpm_CDDW^BPvWC-ZGaa!ZBg(**4,X0cE]&$zXp"##…7!:6mZ#44r ^_BbuF>֖msoQ[]>k˵t=E# q?I 3220lDi֭н[WdffCAAg3)'""P^ddrss-#gSVVIY ~õcFg;w.z:pƪr"]# oZb9&}1\Jå!HasgO{qC=LIOO7 XM&e;I)[]ÇaK/Mp ԩSa!v \*f8Oc/4pqQZ5l޴ :t4'ܹHMM^/ 99/Np)))uL8z?***q1\=7p ,ZYYaM+cHB,x5~.}" 25Mb߃T}R*i;Bitm|m݆5kW#-- <<< ly +W,Xܵz.\ǟN#;'p9=3clK%=<|DDD">~ul,\ ҥ矋%ꓲUI5T !„gcWVNZ^GNN6+FZVCRAz /Ŋ rzZhq ȃ0ɓX|)PVV Sb'[E-d2T*"ۃHiӯFdD+޻T}R*iƂƕ{smې56ZZFxx8^~Ut/{u6]to!L^{ ???CꓳA{4c:P%v._ wШ`'BeM PQQy^tb޼sHII24m sf̪Oɓ'⏋ 骫!Ԗl衇x 77ŃF>T}rv҈7'^Gճ~46m~p~ڴ۷ƎFdD+L,b,kg̜vmaР{ 74y bUFu|DGࡿB^LK'gC?#*5/^ 6F,> B~ѧwObΜWDE>9[mijcRrm i_Co\D4 !BܨBD]M!"p c!9˜P BS!!]gB! #"BD`!adB&Bqs@H(!، HL> ]oZ>ΝWj~No'u{da!؉ _7 % ?(s[pR2DAM9ʊ2xARѹs'xa&mc^^7D`7 ,<oo/_ѣGq-QUS 쬫ϑG^/:T%]!) 8^}R`Ȑ 71ok3gpD"Cy7VO̘9 \?EAA>ѻwotU]ǏٳA߾лOQi׮=v| >t5r j5qmZTWpyL0׫fu|͛7??_ A+ҥ ._nݺ㏋A(.)^Gd)))xED;<qhߡ#Zt55(..g7]9;~JKK1hִ KCrP;!;h!׵zzT*:r塦F;M4;o:t0QUCVD3?:th_~Mx 33w9W\Ν-t^GM_a{^m,xX#@YY9>l&}Ernrt;ɕw9 :~0`j{ غxyy!zFh4zR 5k2C<0$1_qx;Bl۶PT啕!,,wIi}خ}{?RS/aPyhye+iZ||qwcQ]uGÁ1zzEmYkIQy!(>ފة%(/đG ZѮ]{\t ڵEUU[!99]t5L;Tre~+\|ׯW =#itt;ɕ׾}|ͷ(@ii)s&N-$'bOpm^mnݱy;~:: У8Yr7JJJjѫw/r1** G~~>ږ:l(ի~'C={ 3Af͚]۶DXX84i]M/reNη~ĕ+P#<<=< jg7.$W^>}x [4 uTȀBƤ$MVHH@>cҾo߸h@~~>|O==^ڟ~QQh6?xORZjFM?[: /T*| :%-ӳEsxCѠZCqQ *_WTJ7<[pc,6.f%J`df87gk/s7ooo/4mpzԖn'ZEelg_~9ӌ B-؟@X3s]igKfQ\RRk)+@YyMR b޲}2%&ru`48W[ݼlG\y@2-= I R0gB1w.$P7&B(!A~֮@hp5ӻq;:BBUK9@1@!`0ȀwOϷ6LxYG@KoR9rQQѸ;#\@ 4n؆gOo|>DDû()Tb{U*$B+B1@! G$ u6!02@!"ؓ@HHCo7!`@H#0P BB6:Bqo @H݄" #0P BB6:Bqo @H݄" #0P BB6:Bqo @H݄" #0P BB6:Bqo @H݄" #0P BB6:Bqo @H݄" #0 b@q7xl pBqs4lB۱݈mV&{V mb;$1N z1ÜBA)!B-#B3@!"0 | !D@H BL $G@Jr2 M]1T*Uw9΅@=Nԩ@T$&G`ʔI.q7n  BJ l{uB߿? t۶muv)xIֻĞq=7{&7'Od+XO>>jӽ6`򯳹!cO-\p ohj{(Jz7QP_Op c6b@m8/ ]VWCGV3"@C J7oI{(lSҐ"zkNiSO=m5z{ad@oGdb4T*՟=q55|qze%`ߠwXFk⨨@`@KTTTyK?oԥ] "6OrRG_oaذa 3gΈ'w޽{1tPxzz" =\N?ÇZ–-[U@xQVZ-'W;cO࣏>{nq߅R{_H N+7zqCi{x6|K q?ŀ ڣysO?4zK.!-- DŽ D˒? 9s0aajhjj*0~x\rGũSصk'JJJwлO̙=vi/^ĕ+wJܙtm4~1^{b@~ Bn{ヒ׫ !> t[aĉ(.*^/@PRRECh F~~|qQ1:":*׭^/ <,:a! 0\Ѧ~GFUU5_B=O?i!s/ŀh4Øsa֬YFPP^~e?^,wލx-[DXXVXǏ~K,ٳ#э"2T>ˢ_NԱ]Щc; c ~8L$[k {DHr5 ॗ_…)r={js)?N yXfA]o޴&%a7o +W.7_~-.\}S?!;' rz4 Rґ~%p^ǭ_F6m"0zX4o:t1c!JSz(ac=Ƈ!m:`jzM6EVVкuk|}՟\7^{m!BCàR`"{t,^7aM6PTxW0l`,[َOw ""p^2WըNA06QPؾ#N$spӵ9y&R GYY4 jjj k׮ax 4-DDDСC] aOٳ>ƅ pIxxx`Μ9:u*>F#,G`El ,ɿ?cetj{lyqv|>SVՈ0EFF!77 aC mZGkryְ^JC=CD^^pxQ/h:YL0+Ph֬v>۶m34iƍW"33?8&N_ISGرcuVtªU Z IQyt>,˃ gAQI4>i3>թhup˃u1uuުH%j,T$@%J RQBPwg{{fv~]vfgǶߧo~/R?zacƌՌ'{zȣ3w=X,Ҫ+ܤ3L;w|aV*R6󟫾^twjذa=ڪN:)*pʔ)y} _(;_&N K_POvЏ{=ci&\ޡ.yi=/dDe7Z#_׫]7ou] .}|z衇w^۷O>&OT?jnnҥK|S9/Wss٣}鮻YgGv皚Z]z+ꊕSO9眣3,V[l=@Ą x޼yt M?$4ܔ>s }jk3fF+W }bojj͛5c UUU)(h߾}ZFuGkϞ=y立WGhҤɚ4ijkiɓzl2]|źԥ/;C;w@س*;F>j6nнbrJ}ٲeKu({٬-}A_q%gX?30B{ђ%O+ |ӕW]K]]p]x7ug+s󝦀ߋv-}!B$G:a]$ȹr ^8{*ksvڴiZe>Lg}nݪ{iza c$& ["49J]n}ZZխ.֭[}5u׋#z Q6ټe$)IYV[9;mty啎A4z\e_]a!@O=5Z _dY6ٓ Tz]u QAWqa"̟nI'\rX3?)zKꪪdؕO%5['CRֲQi'imkk @݁ l. eB O $d2Jy5kdW|$鈷݀`,jXVe-7k{ @Vօcmp0W_UU:i7 )wUU*Sj̆ rky 2}*Pb-+6n@1]z\u @9̈́MUU_UժJ_$e֦y4 MSU鴪R)UR-)y?MtzgdMՖʐ ֶjn@Y@с nt' OժdzUW4 of0rd, CIJllXꭄQnИUoxUϝ@:R&)6(iΝj>bDϟmO!gޭ +8{ÁoU[  Un(ֶjܐ!op쏃kk;;6A^54wN\~wwܩoRpE!7Y*@8l![ 46j-hGUieS)e>fUcOȳF< ! 2`Y IUZ.lD@1Lտ7x"pGv;aP@{?LS9O#7$;M"3=P*CP8l6/\4 O$I[)}מrJ V{{99nVdMiB1@!B K!>Fuu5v}zDtT8ztm۶ƣ6EFZzhH0?0y lBq[(29 k֮Crr79r^(tMNM5t4h4J(7;29L@!N/(F<IA|m&{CY;oC ūW"((pU\?@EE<0VF&!/X6 R>} _^zcѴiSDFB^DF| s/Na萡Xx) u.{u9@A9m{;֮{ :t`!8OII ~Kwr{V1̤(**½/68{O'v0w|ػgVB&M0g,c٫+ 2Xp1"""hp}0w|tZZK/"㍷j3&c10oH_j'vak z/@HH(z dfΝR Bŀ1!8{܈;* ϟG߾ĉ_ :~͚f׮]CÆ @8)((,6& FIi6aԩS>_~=(T*TTTM8WPI4w&P[SϷc>oed\6uXцJKKѸq?5wBB.\(6/..BARAt:֚?&&d '~󑕕W+ "D2zҥK&6%&& &׆Id)R4l*BQX );va o3ѹs5Zhժ̙3m۶YD0g3Oc9xиqcTTM7=$$MV&ȟȖ  IIN}Vbp'RL2h4k ݺu?axwMzsf5k_ZY3ׇ6=YL6/E||88999&3l,?@Ra2tDO?3 ￷b<K,CnMzݺFyy9 zSL3OM}j)#Bn'hb*,Z8>X77f~\x |?** IL<sF _a5$pA5'A B1@]!`oݺuÛoioAB(!o#`P B!bB7B(!ċr#RBb*3㿕ۗr%80i'|<"*2梲*~'(=yBZRztސg2&1;~!e݋^TVVb׮LG-+Ky!DEEjY!u^+)BbM>2AA@??=B@79PYYO7}2b84?Z-8M|#R`1h48_`~p9b BĽBkJe׮Lt={ķ~>} <<zŽ;гgOtwA,jhL}ĠZpDF޷}Wݫοx555WbyhӺFagPVVʇb e oeee8ݻwǎ;#(++áC#૯BQp,JKKF,.&L2^~9 9yغ+DFh‚~p ! 'z!g8B@@ JK.@n?SAЪU+ GX @UU%&璒@|wx9m|~~~ ömѬm  qAIj6mɚzON5}$f***PXX)S&CO0>mxMk4T1z>m*٘8a<>BA@(\ ::i&tԩ><ؿ?b6܀WoAV@tlrdzͰ`<[ސ@w<)ƍ}8L@A@@!@#6} N} A޽{HI)9ciΝ]ߛ6fX#F`Ĉ6j^z^z U %e$voyymV"P+|iANZ  4/_F^^.*++.]I&~P P zz0vj!!!ܘP P bN&gH(~.K ZUPBBB((((!D^3ू΅Bȇ/IL# B B1@A@(H sP  B!Pu@CPju_4{-q vA`LH!@(WFD7>qx-_"B7 ^+嘬9{kN;}{ooө[{Do B(k뜷l)CPB(H!}Gy40mBAuذ@ap {Sʓ[P PT!77WxC'Y\sr#F!,]cS>Cg"SJ@/*++kW&Σeƕ%ؼqoА`C1u1(P"ɞА`^F1@$&OĜ:@$ Z8)8,G Jn;&!epgjqlc ˎA1"U4#s^gG8*2WPڴMBص+:GϞ=V8^zAa +j%oddڶk׽:։"\\s`_@'0tP,Z"+z;T sd-Qbj~^f np|!wJAУG;R=I[ٹxh`L:YիW|a!ݏ~BHO_PY"G}{4ΖwTsOKK!0OӖͶ7{NYY@f&"X#- B~~~HLLP#ZK-GTTT*ƍ5W٧/ JE`G0BIe"<"`͡:", k;==ݮ.svXz'<q% bPYYi| L^5zh47 5F4p%MLLŋ$E(XsکIIHailKVp,M#V=^S 9iiiDM! x><<yyyhݺ\DEEK A~~>Zj8s   !w\uM !'䚲 S HJ>8 <8th?Z1Zc.ΙKyx=ٳfG:Æsj T*fϚ!CJ.&!`~V)R1`'wTQMB6mju)h{xCѬi4?4mڶԩ L<#ݒ <<Ӧ`dn!-/G][R ,&d{Vq}ǦMCӡ{&=Z߿?87Νs[FlA!cXzCe7* *gK{}]n 7 R!mz׹iv^:i^Ɇ0^_52{Yw=b@JHڕ $$$ ((ELlSb@?8>Wne#f 1/OKTO#hC}JGowzzIsþm-t:j51h`\|yyDDdt&MY^',oYP6:ZũB>_>=B1N^V#$$"ӎS/\ܝO HK ׉Xv!Su4]dVNj⸍W}k,XێxUG Bqanccs#WܭȊJUgkXzؔDBqpHfY#cbpr=[b@BqZXrb:= N}n.՚*=س 2!b ׬6em?OE 6$D-% ZY}~cs,/ t/bpքTeuB1PXґйEql K*Y! :}P:aM,I2`!b-s֗Oγ 98G)G[~ cMĒ5(!!gBr9:( O! #.(tXrrG;!b g{rO/#)a !k` BHNg;bq! ul_{*!b|\=8;h\Cޓ}V"t G0ݱ5qY>zpQݻGEiil Fuu5ZA(B@,U{+~}DU=kg؊b1:@Eus[ng}epkr7o֭[PYYi\4#&:֭_JEшP]]ȈPXp B1|a`o_dzA չotT-ؿ~eW "t'hw&N!C{1q4 >W^Z΋' !>BvNfΜ6~(kNQF&XB F<% iA6W+}\LDq]2ѡC{ٳh4ի:thݻ3`0iKB@@n|Lz&u5qh޼&N*@dD(Z-bc"M_[w "<pi>ސԘ W &&8qg,\0YYYzP[[k847GV۬CϥKLlLLLŋL 7 DMM WXrPֹ$=O:cA :>nv8lˮvݞb+rH! %l (/@XxUghV3^A6-MBPPGVgΜADDը룞y؀ƍmZiGXXѺukeRp6oS!)<Hԓl'W,,o,p֮H"U{@#>>78rK"'';uxQO1cѹsTVVw3Ix 7=t:G:g+X5j̞5}aPFNNڴi#ks54lx QXXŋ3r5kVAPpVXEHcX0.WJY31dP2pcT@jUu'ЯThS8!,9aW96+D= ::N3o>6me<6+6[kN8v(2xp+oкU">]&׉ $w]:w@&M0e4P:o/_r,^Ą8<סҵI/V-[o{ѥsGJc@6m#=B.`))ML,Õ">U퓰xB^GlA(n){@b}cӦн{wmmm-ߏODJHys z=d|]M^3fo|^6FAZM1yJksc `eXducXz=P x@H rO!Wro6gh{w&b#Y{_S Bŀ9+72ryD@anyo IDATvo8Bŀ#P;_Y*a؉B1%Qo01}05)D !0Z-yr:.{~<5)ɥ@N}7,)! wrLNTv''YNoɄ"B\*3㿕W׾OKKCzzEʲ s LNl&xX\U+6y2Rr6 e6mp^]ͮ%;}xo*}Ҿb~_s{;o:[Y}#m>v28~FԤ$MlapfKm[K6SYz~a߶Waŕ8R?ħ>AǎIH19kZ8p>G c5jd^iB}JP gj#3v :x AHq.XMZrXRyYY&䊞ZZFnfϓ:U7W.$]2ѡC{jիt:v>!`V5,{u9@A9m{;֮{ :tDdD(Z-bc"MիWr6n@ZM\`ymÅؾ+,Yٿ#22 /N4hfyxեǨ⥗^'Y/6&e;w|tC ŢKl)Ȁ'lĜ6BRW3Ir 9a@dדU8eee8#JCGXnJKKj32h>|K⭷ԩs.#6&o,-4i9ga%X CDzL2.\KvI*oԩSر[7ƪU7,d1WBٻj'? a c _Jg#bEjވ# ~&2 )K 8y&%,bE?ADB/Ϣ0 hZ,]QQQPT7n<֬^UG<;{|͛CRa+ѷj1wݴ PTXpUO7_ 66* -aĀ8G{R;{qvup62@1 pcf#(({fp DMMM)..B!6&Qhg;k2Laÿ^w?:uL_D.^ "’}Ư_t FMLLŋ$?q+eړitރ'.v)p#D)A@yyBȀ Á?!l!Ξ;b_$įX|%f̘N'0K9sbxW!{u/{IOOW&F߳j V5v]pp0rrrL^=YL6yyylL0j'OUȑO`֬8˗0kL?l,?ŸpfϚ!CJxm3ݎNד9B"LDGt}۴)BBBd KGdğ/{H1 bѯ_e{x߼,um7饗Ѫe+{/tؘX?yTGDgdtKpL6¨#B6KOA= (7}O`Ϟ=ٳ68 {0YJޱ~ZFAZ?5¤^Ƒù"|?xG9bD =bgoVyXd~;߳s:_pp0V^cG(m6ޏ>%Fd)ϕg#:bB}!ۯvTR!!bǝzOE#rDN+#+˰\ P z x˴Jg=@"B1Qg˳$g)A@!^3yKDQqVxH2!--_DB@}05k 6ޯ&O$ܙv2 mHNwBgbGR+3)GE{ :脉+vB>,,RԋI{,BqlQ)|+~7Ge$&G[IQ9;ǚ N+!d' F(s2 #;%@}T볲T\1J?"Իîod\[܂Ňx&p%n|9{h[G~w{{]PrQz,M%MmCNv6H&E ~~4UFSΪ~)qڷ73@.-zrvM;WAu9waXk&b`;!m8a<*++ 5d (rgD-uke xa1"~ :N!I^cu`t@n6D;z*Z6#c=gea;q_e˖@C 7/g -qPT:CQط ? XUyV+&Afj:u ۿމ.{/1lp|6TUU1"3Bδ3Q nF<λ@ii :!mܹ}!fϞиq0f̘_m РmS[!!!7ЍBqr:sCLL ""1o|ߛ!:Wlb O0w|4ơ7_*mZm5Ctt4 stL $9pKBNp7{D31ѷЯ$@tt4VLǡ`wqc w[S4kPXxW][[k$n,(--1yMqqq7Dž Df 8IY{kkkixG 6~ ^1Lkah@lwE_K܏f8" ,GlFY!::SNÝw36MD7:ѹK|wac]ENLޗ-[Μ9:زI>??Z=oދ/bc7`4jňȀzv;<8\ZN~#Bl+ajQ`Ϟ=z/\hӦ|c#̙̓N' ;;/Mzp>(8999V;6.?#w^,[cƌ3Z8\?_b̛;19/զ>Ι3(--9s$CGÑaG`_pj!}9M˳so 8F0LH^wC=u< k?nxtMNSODV-0 {n yqZs.Xh)͛۶1Btդ޹K |zƍ14-^|qZh^=#::Z})}t84a}t6o+9LC޸=!|op2 猆 FP"- rKCƏy??5&N's_~׿zΞ+„:uڛ3lz+ΛmFϟ޷{7`k0DoޛP7F~hAd?.9ɐ_tRŨϲ-N!૎U{+:ܹ+~tpNöl9{"QNg!"sA1KzJsgX_  %L-=#R!^iZZČ"yyr0P!acYMm=qEvhG|Kز.Gtn,go7(W8x;ApYټTYCA>c;]h3M[GBb"ŀB$,wW=LEn\@zvj23qk~zu %g ׷g!>E}RD2 ի7ŀ&v?7ѿRBmbMy RӝCsGfhx.nNAT*{ac١ cGaqlc(m[1 B1@9CGF<.0D(yy5i$&4GLt 苭[t ՈEuu5Z5K,ƻ؀Ki?)r (Ɂl=J0HIBp6p|8$'`C̙zo1ΞG׮hРb_Z?ŀbŒ'fm#GcҤup_z C-46 G?RѲl][^^YfuD$a5ؘHhZD"2"TؘHԠYh5OSO>qMa>s٬O۷޽z :* wPSS#j/aҤHC0qx\r`>kJi;z1\ھ2'ySTX;V#// ~edzc8o KW%_vj:u ;v~Gp&g PPX,j繂:׌4F?w|W)ޮ/Z`>)4i"^~9 9yغ+ ̧݇Đ:),Xc3v|khyО2lh%v55{w{;r0Su=ZXL ڶii"|͚&IXX6&&hݺ,>fs+xиqcTTM떒ekd^N*#*:hժ̙3x bλqE8ܗ < x?$'gϏ¥Ku~{;'|ӧME^^ ;;'-ֵÆ ǬY3p6?/a֬sɱ+hӧM5)[ϖc~eڴSЪe+{/tؘXùq1veį^K/DbBPt伵lگ_cG!5ˑ-ݑsWO՚rLYgsj{ƾq lr ~fU:+z]+C.Y x =.q{DgT'f֦z9k[mMȑ@Hŀ krbw8[1o]Q婨W~ ڬG/ʼnB4Э9{>G'y*N:vꄰlB1@f[إ819! -<'{Я@HձXrbn(A(WmM!> pt )3wb&Ê#qxMgRb#Z^I!F\ +6/j=NKqQus9-|()C8q4$A![0P 0:`1 h-%ESUr{BT萇D` IDATÓ;09ǚͧIwsgޓ#BQur cB sʠ+qdqUÞpĘZ߇l6Lbٳ 8>[R,.P-AHg܏?r |H$~P޽P x -Ks젭'YJ퉻:"haNv63ktlD"?6n)3A`^sę8? %Y)uL^>3br{i[v7qf cA !El>_ xX語'Sm]U6" zQ@ # Hݹ<8vRuuR\kb퇝{)QF(v;RH(E01*PcBF}ϋĨJk;?gJM;ؑv# eeB{&>ȀǒŋB=WG89JX~ :".[ZigJ{_^DFE!9ڴi#NjhZ^^NZZ-yk+"I^,ĜK˵;zK0*P?p>Pp0E LZFIտдY3 <?={NQԥ ms^^{}f(|@Hq*bS7rB &)!~9Q9bfW d=W#m@隣g^\B@@ >) ~~~~:؏?ׯE5j$ZAA~ohp} oikk$'k_}ɓE=ܭVNìYspʫoB ХK) -c|(U{GpVX SNZZ[l G! mʰsNIIwwƭ6µkqPt>;M4_{D뉌ď?mڢq&mm-\Y8x/^³;bШQCL|q֭]  .^*Y}QQQ/▆ aHsvŀ {*6XĜ;#@(m n>D]F@R a=/M:;uPը KpkF{@jN#RF ,< 57~u`;_|jk} ((lgOvݿ<'xᡸZQn{@RAV$J *_OɧD`T\D^pێ"ɉMKI:pWsdcj fg$ pp.P;# `ur9A0AqᮻF-RP^^PT1uN-4Dy:{5ٳ;waá$'k^hTTTP^q:N%)Cj{vWg-U]1+K!@M T%&`KUU󉎎Af\|ŗh{{[h1cG&McI\t:Tt;u88%%'kqEjqeCy/_q`G8Ⱌ#\]#VV rsj:C°g^n187N_h$eQ[[@\X[lE^=R'h[;uBTT4558~8BCài: <)׶n[~j? ~:t} :܂/BS(,u|7{P~mhKw} ` I'kiz]zf:*,pDH0@q61ra:ӧ c())AHH x`7C:uƯĖ/!!!=rgOX:i;GPP 4 jZ] wމ=# jZ-+*D-11ڵE``6hv:JJK%7aD]h4ֽ; [op#g@vAAPT8Wpfy hp`!Fw`#D(PndzzurܗL "+mw_tBׯ_&']*^) I6TUU%[=XV]R\)/Ǖr%e@nP 'A`qg?n-e;\9VN Rq>оad 9}qtWn"qGO;"YLsq̎RUJkc$8>P w<9zڝޡWL[ԯ3`i&"oLI vG.~)]^Nڝa[YlkFK P8@(ܜM@1@yj( spbrFW!E\pX؋3 ƻQ {\ɧIKKCzzb]l=)^W.fyr~F?<[܂z~?]w>#>>}e] x|]x[>{(12^=b;+Z;=6J;F%H$ {z8"Dd)18LPO0d(u5G-%ؐ!!Ҙ-Qa~{̈whJB}лw6"zqג#ıo˱(%z`O(r@H9ZCtG(LH   .1 gzɶݱ5QbmFB fNۑEl 18" 3R6w"U8!!@Wc5pvBB(G6U:̧Z*\W/NDT@H(OF "eY*[JD 0P :aR (!b(PHqllm:zM0P ["°>%@H("/u@H(!BgM@!o 7BT 7O $B0P BH= D&B02@!n D02@!n B0P BH= D&B02@!n D02@!n B0P BH= D&B02@!n D02@!n B0(UI@!QRR_nόbcBH=GSRRV 'h5L $[V 'iѲ%zc:}! J9&t:!bP B!D10BpBqL $JBq#\( B`! #r.9A>bٳp!D0gHNp$߇ׯ x=(Z | = y ={1\@6m-:lco! 0:'"ėPT7{▟?SOʕ+V] HN+V[o4{&Y|TUU!< PSScxI@gȌ??@`` зo_8qby߶m ǹsj"((M6oSKSO>W[+ɦOwy%v*v=\(T ƪ~~j y>Nb]腤hԨ3Ä/_geuأݕt -[(O ̟?mZDh JJJ 4k!N'mлWODGEwwk ׯ\mfMc0nX\|P^yywze&Zj8]:ؘ(hZD!2"p}mMqt U~ׯڵڥ{6ty/ŀh4&1+LHDDD`,e-[ࡇB&MKb߾}{…:u*}Q4n >s*=v(PXX۶ŋ۶B>q1&p= L"$dz8/eKLTc78{<A/O¤I/|e+N< ZmСCر[:t%jU72Z}k֮ƩS8x @ 7/g t:;@VGppc;l!kb0rGvtR8k׮EYYʰj*byoРxdk?ƍCiiD_mahthӦ- AgCL{1>\e!5{n6c6t:CR+FDD$j5t:6lҥKh֬sCtt ""#1w|ߛ }чVߧ>̝;:ݟ[V `DctظC 6tD^+*$EŗN\իWQPPR|G0}t+WbÆ @DD6l؀5kX, GԩS~z7p)УGO̝;/S{͛ǹ-gU9|Gw(O |?5mbЩc y޽GG]%$)WDS"Rj*UڪzzNO]s,jݳv{-تV.,wI$o{I2Lfs' LҒ}Xo)'?/g`8qs㏟}9?g=߾}~ËbۿD/ ߿_K/.792`Yvy/oG GxVfҪUzj%K+Ў;k.}+sF+.LEiÆ jnn%\{ǹ}ժUt˲,͛7O>rc,,o>566ꫯ֮]4sLuwwӃ>X1(+cW?_rueN \<^Ǐʶjx_iΑ]d? )g٬~Z GChZ -*wݺ7uźKtE駟s}[{~H@2 E3Zp cTMe* _`T[[[b i:^݇N< ǥ:m6:ezh Dن_z0#T>\])St7VnG+N&P1~_iM! [gqf5֖͛Kzʚc0V]A!6 z P4Be KR` 办{!' @K~^"6#}i @Ue]A!P帄1.Q 2UB@}o nTDh D2%D! @r4lߛ(Q @ @*Ph a (&FeJB+*PB4T1A*G!.Q 2UB@}o nTDh D2%D! @r4lߛ(Q @ @*Ph a (&FeJB+*PB4"[}`@e i` \(@y`A8<@8 pTA # h aX@h aæ*@8 B G!(  T1AU (Ϡ.*0ㆥK6^[V]+˲ @0Pf Uj>S#Pv( :tvJ?uE!ˡOW5y TaU@@@w0 ~Zh;r@@TǜRx T=V aAX٣r*ѥ06,]\ɎA٣ ^}X`puT^0 0GVpTA=Á;^٩g:*o |EB*,xU 0`s ^B!U܃z:S TP9h a Ǡ A!S T# m>|,CB*8=8ruAb 3IDATUJ0EJA!Z0]`AR( a8* ^@ `J40P%2^9MTS@6̆BⰩPh a ^UBuB+)R440HpQ@AՁU@@+*P̠62 PDԠp40A R + La@7|?gXQ5JL)2rJ*P3}2> apؔB5ܕBS٬ҧnuww+e d y"HTg) ͽ IR&Q6u@]]ӣC Vl&ވ@ԇbJ"…'r9kȑj0 tC_* E8=KS.SMM٬rs[mm$)NkԨQV]]jkk4[ysa aU>x˖ɲ,r9gfJ&Q2t $iԨQ;vvءj[_* UŠ)WgP,S,Ӥbdwמ"[eYg ш#_{_J!]=qĉ֭:!t€쭵Ueiƍν ܹS'N x<>W:u( QӧXLx\XL[neYusï-& ? 555yAŴ{ng?>e@@~֦kO~z-mܸ1g̘qcN>]ׯW[[쪁m޼ٹ޽{㒤67Ѭ˝0/7M8@U !ޮ?u<֛o|~I'UM6͙2زevQ%|Gs*%i+H8+r9q'M4ر#` b3fv?Z>\2Pf UP~ ;HreӜ9s_{YBeYbN@"pg„ ZlV^彆STWH$_8uY~)%Ÿo޼yyU?y;6ČSǍl6l61B#FPCCۧf{t]wETeUǨA݃vsJ`ܹyU_~P`9rƍ'I:^ao̙ya u ,,ٳx mذS&߿_, :paճUJf @|کwbp`3 !,K'|^u+kϲ,544hݺunO0 +Q{ `4{€=@gv O4r5 lDBdҩ؏wXC7Ugw v^}}yZZ)_=~!dz?Nv=;<0PEZM'xŊ mÆ[:?ïdl"PKK+d\J@ @17m  3b bAo7H$0PSSh"PX@P/l5E$2 ǎD"~辤:RV Ž\Z~6)cAՁt:{ܑFB e C OyPP3&D"}K㯺J D`әgE֯_㯹P+ `\XaM^!kRL& T P J` AB؋ S#.>7p"E(@pg?(L:FT3WX ^Uxk050 M眣%ry  a WE5€_:˩ߙ&`]uuBAP0O1!o A Ԁ2U 4L S=hL! ~GA/$ B7^6U2J!*([ylt$l^}fB!gDi R! /NDxM 08&0 ؁=R%~S z.LTLE J8{2߯i7NT2 C9WtA#g#룜o?s}"d2PZ.uY4 8 a J(g (ŬAŋy 8Pe/&XHp5؟+v0ᡳ9x=u_;0܁t0≮f hf(:z.>^Ղbpi O@ww7o,U *:xXbx)a=}xQ~! o+6U(‚1M)K7׽AXca!(UBVҴW3555M`Wtm@eó:0zh'$ c<W,s@J lWܕPhUN! ͕0giuvvf@ ƍS2 {sfN ~w(pO~ߋ*AAc,?{;rw xw .S4+/~7ט/{O>${еAXªͩt:xac,ЮU%y~A+`/jBB^A`Æ j|E4fg𭫫O&J&Cv 2~ ^A 4_y;kVSS#Fli@7Q Us;kfA }z˫5H&2~/ D][\LȮD}Uؿ;k׎:~]jZ|JVxpmaCrJJ n ,4>_aax啗 ۶m  DER\k-[ġ{]v`y:ނy7| Aii) G=|-ZǥKШQ#j?+/Kݦ BQQyp_nć}ʷ_!?|7ĀaTWW]V8Y\F@x6'Ծ>Cj_2"!ٴκTVV"66Mgِd:UΟ///Cڀ;0 8˲n6 qqn9.@nn..\EzA#3gڔӧO]ɟ A]][yҩ+uAPyԩ3z;Fo8*]t寱(**B6mǏGTT#lZh)X'GĜs߯iӦBE|=,,6EFFI)%?ő 1@OMt\)L CqqQ]] 3ut̛7{ur0cLU;H A{ołp={0~Lp3 ʟ0yӘ|>Bp}Iه#C6P釒<,Vr555ؾ=eehݪBCCQUUO>9X ɲ"›‚e3bg1@]`sr\! GN>l(l?v;݋ ᅦÆ#88XBXXq~~C}$ sбc_}H[nE޽ѱc|8Nv8=/_˲p8/ЯooF-*-D۠y| ƌ@buABυ@ gϢ={֭[c޽8{,ߏ~7oFϞ=Q|*++EXVX,X,l6XV,˟1S.1pM().7A !@EppqڶmI6mq%bee}xͷ㑣X,3g#"w((.)GYzH $}LRRncƌbg˲GBBg@R٨FII M30qx75Tc33ӇA !@EXXbcw^u]ذav튐t>^ٳ͛_3 ˖c-h&=ݺuw;ǝ1_ ǚ5`Z|ӟBϞ0|$&4ǸO -m q~-"CA@FAxÀa{`Y qqصk~<!9pRQy^.]s{'6l8 .x 7܀S)OEb A@ "88Ç9x饗&MoGH $ E}}eYX,aРq9Q1ڭ5k/#$ un Był0ɸ 1@ !  @r4@-- (A@  1@:A$HA  BB1 3@A  $ji 1@ H4`A@BPa=`Uf},o$ &$!@ H`Fߙ~I(Ab A׻<{|!HOA@$&g8 !@AVlĞ+tMVf[:s#  H ^(u+NJP8eLvz֩0 !P?O:,>{, PSsAAHJJBXX= C DT4b@*[H8=Z :XWV`uV EUU%>bbbѿ$ˊoSl`+"aE촢D7řsA#1@ Q%Q 1-'2D=@V| |}tꔊÆf'nc޽{:l8%?,-eg (P"@؊[/YJPyBB'$ft{v`ЧOt|58-n+_*t~[?E?~z2R'>je$rT(fPN(Z煌y1S % , ~ٳgQZZ={apwzB-SHyv܉? 00mc,<;wc())/xUa 4M<BRM.5M_@kdJó-RSef^Q df҇[~=RRx! eSRR3`a0nxZB?؀?`/!cE"H :୫^j倜8p]FW3 ^TRDg@:vDâ!!! )I&=u]p8l6"## !!!.9sIII)))8}"7ѮaU''䌯P=J* 8p]@GCPYqJV@UU5""DGFFm۶_+((@LLb0M6Ǐ#ҥ~%A.sZjGf[FX+YA˵ΤHzB$%%a=sԹ5ƃ!11 'NaYxGGðpKA<7#}{&MoGH&"p#{*@om6yM4>9Pb!7ycFb^ >t}6^g}+Tz!;77bϑabXw~OeaXAǹsPXXDEǠkh֬ۊuA`#5M. -65ykA2D@|4m@W#oX&@n!\ R-jdF03[jooDZQpPІLA4AP!pk:+5)<7RPe3 H  -{Պ O¯Nx#,הqW#WCj#' AQP@z<֍p!PPgRC,.s# a)z;i QVxA t3z˦䌭^!v9adu=HAb0X)K( m8$èmT j6P"D? Iz!A W5(J\r3PiWzԈ 7%@]W&TJS|ϝĒBe  eTy\690H0z% ֐*oPP A@BxK4ކF]ze)7#w~ (鐟!'S#[׉t<&JW<?bRAAbyx x\˨=@(m7EA9$ 1@B@I9lPOk| 59M۬]BC# 1PQkYPl6aԼI g@Wرkp4}{1y 88zyF؞1nu{q-n=[`񨭭DGn#>.Qnef{:v@TdرcϿ#)%b0d())﩮ܹqcHNJ5%눏9yeu +Vf0 V+^h5&nc]غ%}r,[<6 NjQ\RV?|/܊SF~  ?۷Ǣ{^\Gŗ[ể?TWVBɓرs7=,_[{v܉? 00mZ8!%Oo"cPNE8N/BCCv ڝ\| ]cEn(fϞY$ /_ŋ*caX`V03{|IA5|vh`q_4X4ѬZ_KP6=.)9% +EHG`dgg#99M4AUU5 ߜAbHFz oF&#82)4dQ IDATӘoqf  Aܹs(,,@MM cе[4k֌'&z95KRn*WStBFTS%KIrył0ɸpPx1tj}X&'(bFBgVujF?yw$  1 h-j1Fχi=c7PmM0 MAb  Ģq.6=#%!6KaL(vP>Ab Ey"0jzZCK1z( @#/ep̌vԪW{ss 8im9 ~Aod#= ZUGu:ȾԻ =Q  1gɗMB!Q$4{,d&EzEoO Al99G}W YVf&rj=cTUeG妧 0:5D)ao333Qkڑ1u*,C?bm=r剕%t_mNСjsr~񼖾>hߘ'Ȓ/s&':rEck<7Q<ת-6'J Nkb4-T޺ê 9ϓTd!##7yWoʓj}S4NKp=oA MˎzL2N@}NmMOMS P=.0#v=3Abϐ3L'B[7񼮂(#WF'x H JZ=`3=AI?x >G s##=˽&3U!s[pM;<- @= F~2= V<Z`١gATA$ 6=QmvsBoσ,6Qp8X,8x{ر~3g*γ,GFب  Z{]\p~SH˽zelWSSsPVV֭Z!44UUEi ,+")TҥKHLhEŰX,ˤO TL46ad5UӨN~j`j+a#D!y'555`)Ç u3v{ņa,!,_  FBFA뜼j$F %:[p'+3S4; 3jpپ=;v@޽al6,oƽFdnwl9^ƉEh&^ݎh@ ^Ļヘ{ڴ)_EKkĉ":]/K,F^1DEEc)9j4VlyXylx=\tO? '/>.s0<|'p  .}!!!ObPdt)c#`6+*p[v(22+gϞEii aq1zš5kPYYpa2Zصs'>SW^´iضm;KEnK8t|e5ksfcx|=r+S3`Ѣ%x8s4V^ŋ^\Gŗ[!4)V X}X'cnX,L/ ,}~ਬzW%mZڛR7|"!@xh")-%%Q(*\=v= 111`ƍǪ+7+$0 Ywnc޼ jEiY*++wCw>0 -Z"> OaxR 0/<}V4r7jkZRB  |V+ DghXm$&MF|b8m6"##yks iD|\4bc"ᖛPR\Vf\\4믿=wa`Zt/̋N:Ĥ?U!>Ϝ9${SRRp)EOb0ڬh|=Ɍz6=Kb$88ղB8TUUKzz\JxD;%8q%nA{B7ƏGbٲL̜9,**/""EEEzXX۽ǏGdd'1@1e-O݌5S R+tľL9hx$%%X^vGBBbπuw{QXX %˜8a</uug@#3qΝٳ$Zðp<ԩS3{.^}O:QQݫ;ztH̘1<zPAQgDPf 0` x;wDՑG`ǎ8qGePQyc<͆i50y8!,.xG$6l8vڋrعim+/88K>_~ñJ}+W#/5kסI&a1X #v=AQP $W1|#;;7n7|7!;;e>|d0!1`qynɤ>WEeyZGfV9A6@ =k  Ac!*:, *:COB 138u(ͼ&M|=Gz{u jC0'яKAł0t}EN~MVB 1#/~vPjܾWuR A0J\A$3伩O B̑F#FB@ @_xQkݨ+e)'AS :}2AQ[m3} H Q7L m̜z ͎-03I`lOqPSY.H]AlALje|U(z$7b"A0ʞ`35#kֺx;u H 0c(%^ #=D% H ku+ffJg-ϤC aijDA$t6*f&(dddxQFLjѴm>|3KZƛoNA7^G֭eQWÜk(;CdeeqZg1ZWVVzOXuko?ۏq'5"!13B?YhlXm!2b^ u9 b%3"tIV(a'4A=6yH1l1eVBoS HOM%cNdVhY>(N̚",W{ T@ H 7#]:[\ &ƴllf g#=B\܃?%!=A0T5ӑt}+݄gq}I;wEmN=5%j2\\:cϒXDA8ٳyybRZB}0q1g}p-ھP6'8驩4mnd]Z{rr;gII>ֹ}c0m{Fޱc?~;A8Y%p'$x3r4K9l;;c+y$yN본eFjFԝ~]75wE}#|9uSp@n_~^xr.ױdŌۣʊ n9PK.3JY@qtСEd?_$|#[~c#4$f%TQfDtСPPF^`en0߄?RXP : 1`f&2.Fp0^ 5+iA@:}H ؠ1ʌ2r1r:¨̪G=JLw{hZ!G!t";.naq;3`:Fd=װBV7.#Q!̇e9=zR`ƍ\x /I^uVQѣ6mڄ:;C``X0[=ZUI{kk/nwnwgҹ#.] !f$LFnm9k5) zԡ uU q`|~zN7 عk/gf᝷Rtb˲`VUC~@F̟ s/qwnqR(JkX0 ~8v\w]#<H># @0jh(S," Гd I)GaɈFPPo.… Xh!nj-_cFܹ8c`<>1u]S߭"88݊Ӧc݋k▛oBRbKL0555Ąq0wl RvjU8?2Æ l˲DvCCf9fQϮY!#')0[r b׽C~[pǟ8(K.q((,fCAa}߾߿?0 w޳_|{Cr,_n+jqXj~9z6oQZV/^gG!CbM?֒s!!`]0W]jWk<5.AW}Pk <8>خ5='Ow0g\$$DӦ9s&ؼ lcZ`ZD먪BXXUt嵈+,y#..1ј?>#p'U&c1o$&& << /P^rr]vhٲbcc9۶e@мz)#w73hCm{LA 37™I<Xb ,<狋qв9Z4C()2v8.F Aeek mNHHKLL©Sjq(--u+P^Á Ï<_;t0|8&h{0p0^ WA`TVK;?yu>}~{\\\v܅ {(E'9.]_6}Wܹ[9EEEǏGTT`=rms+ӘJCSвE knn#.]➻лW6mS2Grm4i2Zj{ }zDll~x䑡lZ#CGD^'ujܧX)2@: P632#/pu@ra&|8oa|]wᮻ+Wv0~S?k[L8'Ntd"m`sd ੧&\seWW8ȷ 7ny`ۻukٳ'rO3 R)0@fn=l7AFa3;eBDFFFE3[FFx*mIÈ3S*4EyС`cc/3~2FO3EYY |ţ> _' 1"A<h<}훍z62=RQ]d 1H+8 QVq)62b"hB:t>4b?J@FwkuUѵ (Dt 楃%d4zNߌ) F&-ӚjZUB@a>:f 9 ɊRh=.ϒF Iw9wU$Fi$&U+[ۯS@|Bbm-_^{ff"cT>Qvn.qA Çݒ(jcNygqt)tey=ux2۷:PLrJ Kb@vI|zԣGIeW{Kp`}C߾3!1JOM&@cfTf쭇mkTQqFZ6R"LXPy g #")._ eap84-A0 5bfyo;8ɥKmn1ի;b3 qX,X,lZX,1#6&/_nW%T"#!2.^nضu+}-[ġUJ"x_())3兇""_H PȘ7-h=<@R2ѴA50!ddL __gfL*jRaZ,/8r e݄+ ƌtvCXX?Ntpd)JNTЇ@`z9hmޣXII2Bg%Aj \v5Mn!!!0m [u/7Gbb L0kjjxB `̘8sƍZ#9)k׬En#>.Qo$ûn4m 3w'Lײ;;[".6 C<R'_~퍸(t/Ͽ_ 1@2ZsduԮxFM+%0@wƻwa}[,ؽgn}ߡ˖=ϟwu[/lO? ,]?j =z_nنҲ+^EŰl8^Trznz#Ν;йsջ4O~>+ڷoE >>~k= gZʓ'c+uɔ+W59ﰏ7 JUVViSyRR/qT9/\).>wape\ޥ%%Ht)i\B8 ڵ&>^~ mڴy,'Kz=tK.Fnn..\EϥOֿ] 7/AwF&1f}I}SwsBϑ u#7WRP;8;7$x֡Fm;';;ptժ~4٭[w|v7|UtdWK IDATv]QQڴi8~8ˋgoD-Em۶ Mv7})͝]tu !5a"{n vtvډ%KaSܮ;YԩS3{|!̌(,,a!Cb8QTsbYP绕עyy`<ѵkWUUѨ AII11͛!\r}䓏odGH ^M,g䜷1FDR$Zwe21YHINiXy=еKG4k Ӧ,oٳĄ7 gL6`;еK'a\xs@/_ls~_l%ܹQXX;n1|sgUUkt"$'knn神KOb̿F#).5kWuY.2-j@(VWbL:Ѫ#(5ޮPH,%Qв1qtСE*}=GHE{ - iXBG_Vf~Fx3=Rizxt $GCg'ɥ,veP=1BjETi !@6!@b FZ\%b:L x>9a"Q px?OuB}=OFK@=!дo8a]qgPq4k⳺/^aaG]eu 3xA !њH 0bE=JmU,D`% raBaMѬi5 5~PPPѣG!%91ё8p6nܨ~ކڋŋprba,`YS*hi y齙2PLO^2@#%Tۥ$g@88Y =z`箽 w+/{9o,˂aXVۇ)#4J"!PkP >QR7+7< gnZ .]@]]v;bb3nqHp38.\E qSh<3ΝmaɈFPPo.^vc/bHNJikVeYZM@x/4S0Gr_h*@I(IL7moD֯;GΑܔ)0idk>l#9—E:6mނ?AAXth=7| VXM?/WkWZ_Ŧ[oA8fE'Jعk7|]wm!M *ٻ@Ȼ v2J=H#\(`j J!7Z2:9μ -[ĹV\Rv=5mqݨQ# Zl-\97x -0 fΜac,<8>\xwΞx-Ha]m@bb q,vKbjQ\އn>BLL _۷R@=r#y1vVf&Ɋ]BbC"٧99&^fmD'Z0vf3seYrA;~W/]߀{0(..w0WX8犍,,SN!;{Ǎ'~vE((kKKKp>\yeeehٲ%obb-B7#VHZbLO" v]$313`&XWҨQ#"$$PYYfxt7`x]ZÇ}[(2ZNlZgoWRkPTT6mrEGG[?~\I DDfѭL#99ĊVfx:bf\Jv+/'Ƣ @z8L4)8uuup8X0:Κy"11u/5/ Qxɱԩ3jkjAv7u))O>0͝^X&Mj ̛,ˡIh(ѦM[\~ .W ͅ0=z,?oW ZABBk/Vːmn=?ptvwǸOvZ7ݺw?>m۴„;a_G`,t& szZ\y풻vҤhժ. -7y G>6Ntڕ&Bf]n.f+}ң`7~[}RF_7nxzʒC3}G|Ӿ l35Zڃ}nJt[oQCuuB)v~19ry꫇@h >FR8w-.o bF@&=2alTD1@w&ËB: SЛQՔ%Bp9PAfR!A $ g lRADÆ<A&ABF ~p) ADÆ A 3@~6  aC B_O#AY?@Hg aCTT QQQFSgMADVQQA@V[K^CߊF@V[o~XI hJb LeY>Ab  & Cy L >AfRBO BAΝ;GarK,=w)Zw>vkA33@;!н{W ؿ;yxq`[&pC~-~MICp ޽{Sg@v<<8\cMiψ=8(c1>zA׿zuov8FD8抈=e $X +vCH UK=˜[1$xۇJFhcM+cX#@0K5_?GD}>ġC?F֭y1>0Ց纠wt"`/ѽ{O,[ƍ@H/ş/":*.]B]]zX&>oO!X`ZaZ0 , ._̟?r"88iiiE˓~͸{<(..EKp! 4M4A-~z!;Gq^}e=^X7|x睷W (m/_ me $X ba,Z-'K}>p'1`qp/&/_FGnݺ!//իF%Zk׮Ezz:N8#G <<FCȑ#q ر{B࣏>DUU^zt3OG~ 'aҤ)qDWM{ٞN;uV$O ,Zolh={,ˁe9D7Ë/N; *2,͛ѯoF-7ף_"+ 7-!}8TUUUW_T}WaΜٸ]$'%``Yq1툏AtT}B8At/^uΎݻ֩Kb4l6ѣG1m4DGG#** < ~Ѳp}Yfs=ݻwE0}t<#hڴ)ꫯ !p_a{x%%hc>}z CR71=qz*VQ!bСig4 ҥKܔ?|Mĉ8SL¤I˯y8r䈛߻o/l݆Lide.e{RZ=M`(-Rbl6D =˲7#$8{bboVfh % $! (^(슢 O\C TфU }\%ŅEpIB"$d.U*OI&=ݟ3}Ꚛ~s,w מӦU>W;yM}}{5tPM:U_~|̙3`]סjomm-Hjҥ-[ 6ha/~QoF8o$i̘!C4y}* j2'c޶Ӆ߹Hcƌy:9:Ⓡ߿<}|G;4JWڴn:mܸQK4z.t|׭ޢ>°,@~F5}WZ}رc^}{ׯ_Jꫯ|v]U{j7jƍZ:ꨣ>s[8QiJ ^7n 4HӧO׍7ިo9}SNq^zI/䓳g˖-ӱ믿^'ONaÆi޼yy睵N;i޼y{qN[}w~sբ/!;6mO_0!ԭ3phkV\:聕̾uizgU( H|^A;[^6l%\|{S;뒋/ /7h_6|?iժUVn}/藿}^Wg9@U+zۙ?^O< V^N:IG}t)S4|jooW_)SXW .ٳu=`Hgڸq5gq r+_=]t%[uw#aO0!ԭ3~+_֭['SO-Q?qzIZfv?^W]5_レ9pi8bONЊ+4j(}ե^~7sEo?p ۫Yf) C%BW_Ո#}Mk/m޼Yv/^0@-3F%}|-SBc=V֗'5jgB?n]V;ǎyVZ^ %l#_ߣ^}o='|3q+p ׳6dƍ[uWO<'~iӦ]>t)'c>:Jwq~SG~Z3S=B;[{db ʹP*= 6mn}߾N͜9Sk׮AzXZ l݂t)H,T`AƎE묳ڵk /ji{9WG [sϯ$5GQP%rnOk/-bmذ^:F~3o4%iH";8i$M4J4NMos@GGz{ -[Ǐ BC6[<u?n}KQCVo렃 B9azPbam(Lc=_5yd6oՄxvQ'L7ݤѲՏ IDATz!/oE V\ɁFb?\xzժ~ٟYgx@ @!B݊oGQ$PbN(O=%IBW l^uo~@=C iszSgzƏG:s8B@ 43P&'B_p @sC  z=79 @?AFKphvg!!hnp Pp6?\3 @;^M@s3O z !  @znr~!+! 471BsC ^lg!@fwBg @ g#@%@8 B@ 43@478BW8.Bp%oZ0!@@43@- B@ vymq 1f! q`B@ @#4+8bN zB@ r[5 @h$Wq@r \43@\|W,X#dZHS1kx3("=7eJ3fx;!b@ҠPy[e.U(hs>1~# )xK4&.ƞ5Ӧ` S $DHx4).v=d*Zs!` ? ӺNg3 KryiOOz?=y2\lS{NOM)>+y(J]Xe F?x H>ߏƔQ-hצ|91+В˕\}F5{i+WkzJGzUJ܁KQ(׉O? VOuӧNA;bVyGo  ;W9/+.`^'Iy?ϧ?fM-0aɋ[ HA})-?\ܯyx͚W^Ys&`\Uϧb}]&`[FĂ& O(HF^ښiiʢE?w%qh>!- SX,FU֮MGǏ/y}ʕ\~:g?X 1M $B Qր3e6v[`ڵiF "5HEE!`OD yAu ,GHRqa/"b^zi$YX $W0G}/xm Za,NOEAj䣏jhЃ*Rq x.7::4e+aLp\Fp)d-1pڢIL`}$;իkʕ P+c|RAZ}_Q.n/6=q'm < /ޗ2{"aBOnq!T$/ƅ?$K3 9 r( c݃MAP(4G 1+E)e~8_}N\=w礕޳Z.\GȁTXm02z* Q[oΘ!IjkiI?oX,\4}{Bp\̡|FbD$!x[dy |&0>cS>~.I|LHE5qh8!4s5蹳j[IB"0(q QT1[F8ntA@ @cs)03w}eP|mi+@\K@2ձ+ g@ovtB! H,`pB--h*yW&J\0I }_Cڴ7nGE<m?Г{~(ϥd ?VW-y2AZ ֭ӺnJwyM#g k`(+$IOϙ@H 0H'sQ(0@)a|eka#} j)9qjԖ8CV=~Y@1ЅAY q͂Q I+y|v*LPaD5`-v*9vn <>@~csb܁j`/cFU 9’);U[ȄX$ l!$Y ?ˏ$jok…1 D}Y% 8P%gכp}D)NQ&4edB ؚ_%2!y}9M[3 ?r96HhS">rbMExU\Рq 8EH~.;@a!ٞbSKh/_AAIN+P!4X"*dzQWV@fNtL`9f%o1B8'RmYBsJׯDvA`^C esE1u'&_w5S%n/^-$,\$D-ǯ23!0E2rv!)*A-n@-óD8pgU AuŞ5R1u.#9 i}f zL2iQ\@ @͏>ZZ$+w8D.U[U+eWZmD؋!8$0D@ @} Ț8(UdpjDTLe867mTl$7lWA)DUuqP2s z=v-v<`~od Iĉ'r@ChHڵzRx.2'IHr"s#Gqz=](jHCDb!mT9A-J3*@_FFq**Y޾y܇IzYWﮡF( "c;QE1@#C`; >Xbhխ]P,a` ^|Vk{&J­]`?Ke38AO]3Qؽ pY1u,QR$CQaPkVAɨ e0.Wa*"Zw 9kX,"BQ=Րp?Q Th (Cg^WS CJRhޏ3Q,$LCخ 2ln{Ȳ + MAe sW59x-\+A)"u 1' 6>Pi7]ʂVx\fϰ@(pmDف=/;h@#hHEłhY%B@G8oj۱ w-C* 5P粏E0]|y /Vn@R,m 1jS5\A=CsY.@Xr0 UC3`O8r q*v]]*(Â* 6A p\c Q9|(C8F6%k +*ݓVC@U)0 TF&$meTY" A||K%N9>&#y R֕~-C UCS!PM`=aN@0[5@bV!a*J{@*(k!He=u ̉r 2] Bޯ K * \8 ą }&8 m;ݩ +['.jkeMk_Bܺwϩ$L`vKg0 1uͤ 6A1L.r\9gY7ejI5 t PF QPLT#N2hh `:5--jaF8bv W*s% 3dDAB>8[@|3D$B;8gouu$7` ͂ZS pM*Rɂz$2\B!%H'~CN.@ `XfL:*oVܙwҀK`hNW j Xbб%.!W`s>ωMmtvjĐ!iv 0, I}t5İR@*,kC`ρPerLSTi2!NN*ń U(/ w ʦ-kd5_Te?a`̀}N}T3>xp:<. "#!U2uc݆B PApg>.T{G !0KV9LFX 5࣬Duw~c߲de%n0p܁A{|W?V)a2`+4H`?9cTkQ![D@4A:;58yg?qu ,W.rf HK&5Jk%3 NA>\}fwtpbCX?_owuP( Wn A0 |a`a ̢o 2`j $ Pg>7b!OIMmC[ZaB+~}a(F\(^|(Y(Q$?0yC +lP-(F[PPg>wth\X@ wPPwP6^`Vh m: a/0#X0aqxKx1' b+0tKš bǎ+sg^P)o]aVzW ݭ7::4e"N ̈.Ӧ /, "EE°xR$[a=$n"d¬%HDP>p!'b*B '=re@aXB $y׊$bG+ֈJb o͎qB BϙSzE\mr[I,"ߗ,f ;\(Y-K 80:9{.\~Zed*pjb"! ̩dW,{U+ʬY%CLw ڒUppmD΀Pro`y3,BA+6l(~(˕Wjg=CW-w 4 tKK b .GpL,` LH&X2ytv2n|=w EZZ܁0x¢mDDo>o=,8V4 P H֝w K] 9WA*T\u]jYA¬ ֨ Pw2PwVmܨ ~gCj!CW(4fM`8Ja$PH܀jЄ gHIDAT VkgPЈ!C4L؂Up<  W C Hx&,X'N=UtwkĐ!֦\NB.W*YOEAK[F80o tGsI[O@ @Ws>1@ b 1@ b 1@ b ``aLIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_bool_subtract.png000066400000000000000000000675621466047437300255300ustar00rootroot00000000000000PNG  IHDRWbKGD pHYs  tIME ? IDATxwxTߙ ^  J*EA&H M`ЋxAEH BD0!@$L|3Ô35=y s{=e{_QicbпHR6-~1@QQn݆OO0x?}BApϊ fkӍB\?ۮN886غub۵3z>&6[}ϽH$჈i4He &Ñ#?"3# 5`@=GRfS1Ys5{ BB K!M臐 {F]]]臚(J~roa.!F 8sbXa#{ԩTW||ԪUNG&L&Ӽ^x@ݾB7O 2O8 k׮\sҥ{"7*]WŋXt èEaGPJFE@P%XxK0j(,_5uz}5ڼWѱذ tڍ@[s!v]F^:;IQPP_8kM&ÌNxq"`ݺd=rIkE ^_`ٲ L&ý %ѣ'X6Eׯc[@T",4srub`ɒEy$]T_@NLscIR~x睷=~??B1M`WnӼV__mBWeK=8H$\xcF?_v [?ކ; 4>={^s/gѦM[qZl{o4b 7PgObE;:bί46]pcF?_~= D u~mDY!kvZ]j( h^S(_&CTTqGGGڵB t?$ AJB}}a:k8=+-MDzz:*++4"D}A'zQQM111~ε󞞞$E2NCW26!XQ 1nݺ7 SsRDNNڷo|2cϞhӦ:gx,\>>>(G\vF].bS@@:M"82'(!Nej.6ݿ)=ep&b̘2Di{ɓ?aŊexuzًހT*Gќ82wx QQQ wkkoe鉼\X *Js7233uV .gXkC"`9jk8?=}bz?~ܪzre"{IX|dff 2* +WB}tz{Ayy9g՜2H%R$d΀=ZTUUa]l,QVZ;O 88<0&{Z)6AágȗzB@ƴ@UU>u낄c T*}1ca1ds?ip9B[L҅N ksF S*kׯ-\&ڵ3:AT*!tuĠJRsiۻ !|PWW%`L4JJQ PSPsh!ИAII ЧOH$p}E+P(&#^7$̞2^~y&22{78w6~%~XJ~\ƀVk?{MDоƕ .]DlLF 111l VxJt P(Jo9?Clx"# H0Յ4>z} ?3:7u+h0BLQUUOOO0) UUU&#rllXol\VZc>aH$`5bERoaz@]0cH ቊ GyyBc9P]]BQs_.]G[X: ͅJ#Oȹ+WW"q0!@Q7ujL sqQ+?22T*M RLDDD s.xͨ@^^fϞq0}Tq5e]&J1q3xedgg2220}T~(#7%(hmYFܘS77r"-- *ʨ8vM.@d,7kС} =8z뜿̿' * .]7iLy}EшIa!|\ 4 A?LlIqMg}# b- BRO>:=z׳琐0NgB}-0ԣGOk??Ic0vl{[j//7bs:ƍ 9q\DXk!HHSyfDGG BhX8ƙ PD@ 0&( +SR0Ӏ0 sMiAJT www >BUUѳWojB1bO74' ct, CGL]os٤֍iCh;yT \΍ŀ:|S+ə+XbSSNII"oN26DF(ĂPD@)՜!!f)#\èv`S^ b C@[Tj1'ktKYl{BB1DBA2SbcvDD@!Sy5oɆD(B2!bbbA`ZUEe}-7t=^Al/IL@#t朁KIrRLxWۙ"!P  1!Ezƃ@FL$'%"Fl'!P 0J`5ք 8S+ & `mpP !1hs1D1mGGKa 8 "~b {dF֖MFPP Β)ub`>{:01C) %8pxq  B1@MDY bEĖ-+2ڒ(H'iNp/X( o\{8]]<8RX+6!bqОS!kk os65ck#!P 1bjL^(i3`M uc&g"ДvDwII PUU wwDEEA.M'-$ $DM Qj%NW [-bBI}NI:2 @UUOAAA>eعCn,?7r !麈 ^+eŊ2~(ā5 ͜5˦bzpD-P{{9p b)UUU|g֭ ƎqJiii٧36L9Eh ]{k,,IšKm5w˘׮!LOA׮ѯ_[L&Cѵkg:t -GTֵ3qpi(̘1QmӧMEYYk:0QےhgC4gLT0% X2B!@MII ЧOH$p}E+P(&#dH=v _dd#0g,׮[yy8z, ii?!/?k*84#7K+l1ӐJ!Z0f)[ bl$"Q[gҼt)11!` 777?gud@TU Dɓbv H|J(*P GL>TOY֮}`ɒ)ň syb UUUbPUUe|-Rt^T*h===QWWg0PTT(͵111~@O55^LdX28|1D--PY,L!@O(P^^@:h^Bppy\opeh/&A(m!!VXk7lq`KB̔Kvrbo`(4$QQQ8q"J}}=233ѭ{O׌Mx-!22 W`x :njH^ QDE]P z޳~q[f5;mY II_GyVF)r9BBBJe0P;v a& 9s6:~;( #cG̙UY 0ƣw|O`y 02@[wC`mmqV%6,qil-Cm(!@ 4۷ J>}둚_ϞCB8 ʼnX85^[@7֭ۀu6XUqNIW1ۧ.9) 3gykb^O]V~͔#47}ԘK4Mam:-Nre?v{XS%+1U~gs=<<0`͈+аp$$3ib@Kq6v3uSYhc5f?6Vk գݛ60׆oSLDl{_=5S{@9Ww@m]ډm9T*R)1|";; UUU F^3#`4nLgZIx֬-`I_lZ&OidD8l@m'/J!˹1aFr,z(v~5NڒU% ?9)ɠ@23?B1zϒ}Y>vbȇ%.lg&&*lqf0$5곧 ۲21;YB 4`F@]rRZǩ/,mCP0B1@,aYQ^Һb#q8vKdSbJؒB!XӦ9[{,kgsS91(!b4HP{ 5$v=!siklA!S@Nb!$afa~c &{ގSuџ`H,O3J@ 5h/+v>sTI"fC-b6&#wBhB]tzn@2H#G->8bGB3Hذ1Gc 0vS9b{ll-}n[r.!b "fGNfE=G=,w[!bG 1:3K7#@g+ߒ: !b͜7ܭ+uC{8B8z!1,BI@hFBZ初 Y`A9:|o egEk,c). P 4C{ܖLsԪvY 3-q֊ [7tX :8zN> B4[ިR9ѓ!8jX{ 9Dk{ijh| s  @"{.JKкUKcؽ{̖ #&:!:tvZ-2555 ӈG`?7?(+,YV!`*@l4XGll_{9n[mfh o m1}49{/FӦ!2-)*++MiFj ddfcdǢ0[j#: CB1 !# m` GBֈCeR[ؿ?]vF~?`@o>]vơCnV4̘ၻoN[ (O'27p睷#2 Oj@PJ%Bt{76K; \xO DF#4${W^S^^ CDGE`& 3fLGTd[eee:ֵ3qpi☨%۲^-C,c.A\X⢩,C ??}}裏"-- %%%8q}Q|7ӧr^1Cе[7],7 t!ԣ] ?B^ WL9+)L|/y= &>oz<~;w˗jp}N:uhnZ4|$'ѱ'1|kG9b!yWX+_*Y=ry-rdn"F9.]DlL $ V\:M_cʕH$f-wރBѾ]4&=NXL&Cbr#$$K;wrP*Xx BBB4{}\sC{}1-]hcٲfPX aaa +_\ڪՈ7&Osg6&h`lfSuZْ/Mp(ns嚻CTUUpy;AD([* FLzY޳WdMT*IjƵk&d geK @}}kUT*5["cbbp5k4===QWWG1@9oc""B`F`A`-k:tO?t h߾=A@yyM:sLT02UtksMuu5 E-N7''۷\|A& ^x,\>>>(G\GNN:t`,r-6hIs I1ЀBe3;mȶXԷbk ()pD*J%Ν1c"^yT*dff[F˚8a<{~z艪*޻N;wooSQ^^ _JqހT*Gooodff"..`]Mm ly<==˗3nܓX`&֬YWcXkC"`9jI{2h N߳VZ`8\]ӑdw?^d?Kz\X\.GHH(0tPl߾={'w> =RSS\na!zvǙ3w4__`؃CЫW-޽gs5BaLC=5Ij+ IDATkr2DGB^txeo݇=!,4LtfA@` Gܹmd@)=]PzZ첆6Vl7߁5 D3̧mD6b{lhYacm`Ml*aL~ƾqbz?=!}eWVVbOqлwoHRDA_ϞCB8xxxPA5|v8Lث7h@-YOD0]޻ωYHʑmDHHS[o!::^^^(/@vvB)ŀk1' lvr7)m ce| QTJpww#PZZ,TUU!0(={&1{9KL(v^+y_rПI)T*\.7@M<=UGg˺8lG/:d~}88{ 4dVS[{id{BC=l=ג†vp##B1;7ZAϞr]ˀ !MWX6~&{;1ceG8KGHN .'`1}gv}.G.  -XY[8a:vm-]SwII N>GP(l$by/ b7~0T?k{QH$@UUOAAA>eعCn,?7ef묩AD0\ɅL&LZ.Ř}bb4ZA!fG@Ge;Jؕg b?Cn]0vsV*HKK>Ř 0D:VZiw5!`ʾkP 4 n#6IgNޔ}D .dI[&kׯR-PT8tzaM4AR%Xjy3rsc۱aڵT*d+++۶mEEE>8 k׮\Sҥ{"7*]W`orrdd`8cѢE 啗_>E]/28è}aA`E/!@|k6Pc!}K;#\l7Tv^>+?i^ ??cnjD"%@M߾}qF( d2;v} ̙ߏܼBij}mK:}|}}hzm%VFS3n_ geV`QTt66.\}oo]{s1Yn-pXR)OkS FP;ZkG&*Я@Y[3`z[e Ƹt)11&!&&mT hGJ%^[H$߾QQѐH$XlIo۱cW D"+1f1 )Ӝ=͎,2Ħz }z&1?D UUUbPUUe1E4Nuuu\Maa_#LT*u ز?ذ~-ظZƲ+0|͡ s]v QQ D1_/**Bֽ111~oJpjY,ߙN\Q 6[h7tb OTTT@GyyBȀGs%W#7E&ҥ >h+~={W'a޼PTGNNh_:^|b85*`OG萳vOY"ƱZv9bWLi?22T*M RLDDD :O ^; L6dӧM/jN0W޸qOby,X0ߤ}3K׮]1r(QO1@\* oᬍlg1W,,O\.GHH(ҠRcǎ!,<r.Sb؃Cw)/OH;<9 g͚@=s12@\3*U흋hggJb{뎈 /ő#GPWZ;"?_0:΂<Ŋ[ziy2 3gƥ?uj 3^z~>8xxL9vlMCn^!~܂v^'cS9?`izCDU= $4Q5Xc/i 0PN%"R١hdk9b^{;PGץ.ԆCD0ob~ 4h#EJc:aC6;"a B1L#zv:*\!rm}L9D@h+K;W:K%sM{D!b{,e*B@a@i,0I82Q)70s!M$*AGђSRz`gi̵QGAz l "XĴNIse})):c>vYStArROl|{eM{G#E,cv;=SlKM=?ԩfӦ )2uOǚ>s~sY~=)]8퓓0s,ץ~fY{3hg.P7m232Q[#ēd6b 92|lϩxbDүϑПtA/bQ$b Kw]]-%8#E a!sE1RGajhfږĴP@uuju# v.=Usu91:m])TB.]BTd0xصk{/GUU5jkL^ߢ GqFJn9Yث>W[[>}ѣ{WTWh^kb\=wd|Cb1p)cGF{lXԽnnn777.= fϚe## Z^?$ƙjn*^t+hѢ%NR32gx9fkr{bfASR@pSNb3www x7u|EE.]q&<L|  <,Ja7Z7`xwc9:b`ӛo;;!*-O*ȈpQ6 2,\0qb ٧x;6* 9"{ckO%-)_M#wy֭],Jomބ_ӱ˯V ΁L&CVvr4<H$: 'NhJ8z {QxkV9T*E$oX .`S((,ޚ+<3|L ${eZrʚgtgՂ`ń E>)?BQzaqO> .BDD[xc޼y -ZM 77:!@7_CiioJŋ 44AAX$_~zf16 o $"22~~r,Y(^HIqжm;@R3ؿg)~tg!MҸP$ IJƉNaCaI󹹹x!h6ֵ nč Ek GDDhʋµk c&AS:"!zl)}1͵cF;uIC<i Yg@" !!!3gNb_hh(~Os:ŴМ1jÈhf<2У'V, K,;`W ={~}{3^iGoΦ_X7@ pTFCN-lHI!&6[}ϽS@y>USRz`n+W1!i 0dcDĠH$0`   v B!B(!B1@!  !I:yнGN$.#D"'fCF!9ҭ{w!!8W*Y9n؞QB!$;; &GLt$BC1t SmAPjjjT* ^#ɰr L)O1@!.'qI7:e2v٘?>SA&iUWWxhe_M~BqlI :AtT6nX RDXh : \sE<'А@~\zl}blݻ !|PWWg.C`ƌ鈊l6>m*ʌ3e &NѮ]u]X.YnW9ytwxzx`k+JE_a:\p'OFAarN.d2."7РWs nf̿ީV,_j>14c:^~y&22{78wN1f5Ґ󑜼`>Si;Biؚ@[>BѾ]4&=NzcDDFFW.ĞohΫT*~S,[QQDze+-[`:ԭ^CA{7r?̝;2[[[v P#""IIku:#X aaa +_}LgV1mPH~E !:&>ޅOOpX]JR* F/ؽg/ (R @REťn&(V60@9hݺ-6!7ШC3t͉DZrrD'iix͍8y'j˖߲1ȹVZnn>SiBޟ{>Hhc RRaaaxeޫNs.44 ލ6mښ-ܵaA?,*` |||PQQDg^3g`ذ "8$999h߾=4z,y\*jE]O!Ö=$(**Mo;i?╹s iS eѣ`y,X0_s^^^-o'r9:3g??.^Ԕ3:cÐF(,,ĵkװp|5ʤ1V9[i;BiBؚ@?šկ#C,z3gNc[hO>}EшIa!2wYѾ]{ tz膰0͹Sb؃C,ʈ_~#VX D;wg1U9[i;g ٔ.8+Df i6}վq3@#BqܨB0& #D1adB1˜P BH3G!!.dB!F!I0* Ba!adB1L $a! b5 5232D41ׯBq$C`L>$ٴ-R BN t 3#=?u*6"͸''۶Q B`K :"Q$BqH  P BH#a ~j(!ĕ IJϋ!!8[V P5رrrբ^s4MzΆPzm e22@!.=V T (pa A8e}QZVyMJ&㥗gB*B*_SlxNoO;o_} Br~ ۴S遜+8rj69JJ+2!G>m5h!4)67 U7+?(Z7jf뚞uk1sl?#Iō7 -Z`mrT*,X{֬~{N9k6Ɵ^W IDATJU6maI8v(Ν;z{笭EZZ*+jkkCUV8_sZjph={iZ)#b؞@h[d ((?#:u/@Y_ 5r+tH$\L{/O'зo?Lq6nXKh׋J E&L?d2|p`1JpAbСPOEx':VZÒ>(-+ǵx'NW A=y *v?*ѻO_H%uXgC?5+ַ$݉R[͕w9 :>P ߿,&BHBJrD<==pϽbCPW{GcRt!1~~~(-@}}==ͦVs+JV~b(!ĕ=P=͊#Ҝ޹s! Ɗ+ ҈-M# !Ű5Pk6SC1@!.- ;Q K?7$%%SE!V2.zC!;+ ws;6h=x0:%"i6}>[ DÇDĠf0!8k%  ݡ 'a  F+&Bi02@!N q BQ'!4C@H(!BⲟM6!ҼadB!8&F!BB1@!&l ! #$@H\F!ĉ002@!&Bi0gM@!4o 'B02@!N Bi0P BH3 e?lB!y!8 &WBq"L $ BH3 bB9L $.dB!F!I0* Ba!adB1L $XQ&!6]6itpBiȊ b#ޅvL^Bb`׻ b#afS'rJ0gBJ$R B! L $B9\gB!8@H\ B`! 9#2329Azi~ =2 MNdc8'nwǐ:uSMoAFc!.-#B}Q ~1@\\nfefd'N{=$>x@H\X L2`~)qݼm$HAMg*4{pBbYyIXEJp cb@6ۓ 1}A崺=RbY Y`ۓPw܁3gNƏڵo@FeCdb4%$_=q묬Kx'QVV55}}>X& [@H\Guu5}Q]]:> nl8I5|ApY噻~Ϟ=:t('xW^RưapM*B`xO>^_϶O޻ B Eŀ ~\5M 77ܼ S 8 TZҜ{K.!;;ĉe7|/rssqYc„ `VV0a\rG'عs wo̝3 ;w/__+9vuأ~ݑt I\O $&.A\v OXJJ%O76nDw!0*ow݉{7njOJZ;v@PL4 ee+`2,xu>:EtT6_J@Xh0J%B诹>m8N<^=uÍճ;~>:m=[p2L={6W^yѲ]k.|gX8X8*ߊ[*jP `l?A}}=T*mc&:m=l¢[l'P(Op+IIIزe -[`F3wvBӧ1glڴ Rᮂ\pǏGFF1mڴ&F|}|ʼnp@߾xq"VX5NзGp=Nĩ'''=8h&ݻuA^=oT/;マcxhPoݫqlR##p5ͽ0Uߵkt6œH$xGq~ȑ4?tDd@\iL l!n ]tG}[jO<ƍիW'|&_Sرcei"Xz5իWk^7w%::=z²X,onڌ{ݏ;A߯ΟMϾ:tt=j@H\w?Axx=%paj:w C;w6 777T*\Fv/@˯h߾@}>0` blTޝWUysn0DP)V R-##"-iJϴ}38 L0-AdEY`BLX$9sOnryx}\I큸! q577 /z%Iv#/YDsŋ5~xS#G-*֣>a+/k$qǝׯ\SNɓ'o}Sj.袤3|{?@ <êUh]3w֭[Ԭɓjnv8GNѣuqM\¸ >|X=vs56f6ݰauf̘oQ//_ܠO^y?~$v DW:"%I?s.dT߲FivFҢ>3UVVj*=znr1r4 tTt]P0pyZq={n.5:~h DΆμP]%It#׍r" ;5z:p` (@Q΃ _DgT aÆ%r4 46(t)c߿ qNMi{?4"g[oQ?h&L DsfͽC!r4 pZ !C{kTQQ= A(b409AAC@U @ o]Fk׬ X a XNuɥjȐz&L䐇^{}@@~96az\ `@U@@^0j@UH;0PA! $v a ާK ;0P Y`iYD38^"|p!9B $7OҲ`@80S Ph a @nРee1~p@0Yw40P` ^~8xB!Ez:K TP8h aDt h еh aC/s:3x3Dϑ QHUP(h a@kgR ,)dh aHA#gA(@D@l)X"@7;'[ H2XRr Au '*oOg\Rf8*BC!HiN6KH!Qu *BiU A=@2@K m@:-\:K!^XH8`TR|;̂L+|T 0,T̳*a ۷a! lI!=D#3  3BGӑ% ={j7۪^zI77MMa@6Aѳt晊j-뺊*OϚŋa]U :/`\Я^Fjv]_L*+׿RT\1HgIa-Ke-ңz50fUs4}4*I^R"Dz cgΔׇw%Ia ]=1bl˒mYZ:a9,˒%^ +_nן헮Za "/k/9TRjĖPݷqL$ɲZÀ~WU^pWmI+i"}/-$гɱ,EZ'&ؖ%w1n]L p%nƍә~{ ݮFq_{ SĶ}$v5%^W~=y:Kaض\/ uJrl[hTզ/5qz ap@0@\Z%ǑVc۲m['BJ}֮ fY<࣏L008-a ~ vKᆱ;wJRY޲@X Yn(br,Ktj5z}Dztp̘$EB}7K^%`xh7%uU^nc LAaEciYN4u־j m4 z~۶5࣏Z`_=jf?8[S%nCL~1缍Me@RB 8Xg ֵTeu[JeD`I@?p Cl$ c{m^smTQ۶u] ߺ5f`[(8)MoaqTbjw,K]SbIѣ5h؍3?jv!Cb^;z9mxIDATzI&$4E>|\|z!΃ SgG} @*ɞ8Q~0L7ʱ,9R;Gh3o%nTW53~˒, ~4dm>܄@ A (/~9sq8nǶMTTUB#m3a`]k.ֺA4v ~yAb%v,rvB@3< QpU0x!?su:|:gz 8 ̞~;>f?8[o~;[k?u><]gO?F q55f w-zO Ҳ2i9'Ƕ͟s֯׾ /ԗ6nY.y|P"^0%I Е{/~lf:K T&8`v%LgpϤjrV|qxcYGj$}y߾ }~АZ>pLe0o{vprsilgV3:F@(@OU;x8l۷.}眣Iy*29L3%Icx/$̟7O駒?$M3K Z \Ihl`N4@%lK"  v:Ԙ-Wx]ox AL׫{zһwNoh0~`_$z^4 b6|:i"@@F ^08 M@ xūޯ;tHMl_jSXRVfn_ZVeM(T|~axLg=.] :TU !L`'$?`[n3{9P xL776JeYQ={Ƅ U51}۷ %x @p*AC~ Xq€}uLKz2GM(x1 i(peI˗k…)38% wUU*qs!pZIlҔ6e~OeFث<߳cc\Ϟ&$*\WK8޽5DX0x3Ax;UvAvI񖥥e[ݿǎGs@UdYQr]-)+O?K}$l [J +Ѫ?x{P(Y2U` ={ ?=~\gO?]3@Յ3MpCJyz0P# 7 Aj$! ^ѣ,KI ImǏ=4\M0y0|W  S ^0Hءj@vuW@7}0S -I3o󻧻wy̿Ny| /zRͷ7`wF@j֭8Nj@̟@e Y ж|m޵cJhhSItL{Hr.9qBgtHDE"iMT8v&A+Tޖ + eHwl-dq> @@#GT$"Ce)bۦ,G@d?t7@pM4g^i0݁| x^CVjטTBW 8کX*vdxTd}&{{τ@-1 :1UPow9@̓JRhS T U32a4qb*f  I)Sԩ,G@ՅDt80 A>xanVÊs&@TL?偸ҳ h0MlO2R.ZH@) >Z@3 o y값DR)ܗ=&D`0 @  ^s`xWO!^PU T*H6RYHN(ree?qBeGnmf^l lA =\57{T8Ձݻ$RwKmu!AWȦDxoW'LLt.hr:;s8uL 4vݶ AoThU ALOe9@ SBO?>}ڞ& U /9ГA `&DХi89rD4䬳bGkno -U.XH0hjБ&ĎpI0~萆˲,F"mU`5 sٮ [5HqY~[a9 8K=W%m<7OU %g `{J<8qyʀys:@@^!ICZ Lp +եdgp&:m&WTB88WnKY Dy ҽ`yU$#Epy R̄m0ڼYtE% n3@@U |$1c4 AuebaE ^Vtر_ N-EeaE *w_4}@e^_޺%10 񾯝3GO@9H50000000000000000000000000000 b7IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_bool_union.png000066400000000000000000001072651466047437300250240ustar00rootroot00000000000000PNG  IHDRWbKGD pHYs  tIME #du7z IDATxwxTߙ -RB*K*%(QA:"$4% ?EQE1x(`B"$H' d83f2s33kaÆM Ah4RgolA~}0p@ x"~{+&M[oOr0?+ >ÿ'Mn!wD\߹3:]RghooE|ΜM7y{[5;c(+"bط{RGh$;2޿N!Ϛ H Aᶐ%hac/1xa@R_|N466M!lK 0 MɱcGD\lظ Ǐ?ƬGRM۷btL&L&ܝIA 9on#}6l8xxxau&xmۈ酋ګGsktbַQSSwuѱeVW_AQYP'Ob%CCC ^@dd$B`6mB*Bl?cFVŌ ŵvÆЫWo*3@2tK=vm;VR;w2?i3·v֥oɄ9BݱxRؿoסcǎxfBxzzbskm׏,_0L!Xd)nX./\@Ƌ/l6#"< b`ɒgH_Fg?ݻcsG K.^~Edf~C@A$ Ǚ%h׮&\)EťQCڵ+ N痣|2ڶmk݈cRi_WZ|[N8qc/ `@MM &l9*u萜 QR+buo鬽 Dyy9l 1116KmC`00 , ppXl)rrrpRMXg5Bv_xѡMqqqpùAAA 88GN] _XC^7{0q}6י>sf3 ѥKӧ=0} :]Z+S&O¢gkojj53ί_զ`:D8$ЕDEoq2D9sN:$>V\W^ya̢a0XpqݶN}O{+V>bxeΑ~Cem(..aXl"//!K}9cǍDzn -\cưKc)x-$p?<(]HoĚ5iX":&VFI~nFuu5F O<1̙h0b())FBBW̞((}m:X3?L1۷h;cjjmA夦>ŋa~Xu]7o$!A^Xp5mڴwbĈ;M&¬YW `cccc^o gԨ5jk=4VܹO`'8~FuRA`C,1w.Va6a4NtApE9[lZ  H Rlق)S8x<)SHAPzAb +ۿKKn4H$ ! | AA$ Dh}c&AH->}:ӧO'!@ hn@jyU1UAb  }Dk"B#&A.$Ab B$jw2AD6eu萜L 1@ZHILt !A.FnlM/$IT8 62hTJb.3ݑҾ4MMY ݢ= `0@~~>jk/ 111/.3<Ӓgst3(ޓ?g*jkkgO&Ν+Ax ۷Bhhn=e^ZYyY̖, \\ OXߜrmWDL?&?f{7~ ɄӅE-JG"D2 Ly$|+{dg8pUd2aРAٳ pc6a2Ǡfvخ۵s  :hll\t ˖.AׄxDFbɨ(?$ Hpl@4'APQQb$%%`0a0`ΞAyy9gu6`1T`gرcKkwx{ya&HgG=h|$Z!q(ps\Fml!#,,ٰX,bM@ܚHw C߾޿ &-[M6g|II0aXDGEb􇑜<@1.88x ph' H @9r\BSz<bRU@D`p/<(8U@SB3er&:Z.-wV"@ʽʝ !@ \"V-D_J \a!/!l5Ab1BIYR'K eO1#q9FNtq%H$fQJA(3p\%#p%3h% A&h/Kx |a|'}rAyɣC5=jwm2lg>TrGi#AB2.|{5\o߳3 'y:ؤIXBZ `0@~~>jk/ 111Np',,d=.-fI輴PB #-W!Ȼ>K]flϔ;`س'Εs|<|}}QUY!44 ߞ OOO޲}QT\ DD|>h$āEsSS.z`{_rE$=G^0~17ƶømL&!p( Pe-\X)1hluY)gdnr4=@ɞ=ٳC APP ݻwcٳ pc6E_y%!8r'/ ̙31 :fJAUU.yiyt)%Q}F\齰L:M#9TTTIIIؽ{7dgg=܃?III(:{弞!L&';>k(̛ׯCIq1Fv(.)AzZYe@.2\#P#]o_e8Br Clmk+[[w_ (yDԩ<`0`ժUhllASSVZ;qqq f((FVŋc;7...]Ab{ ^'$ضf3RBƗ1\"DhKf)mM 6 Q^v !!?UK.`5 ,+((HHH0QXX.]N> x"zDTHrjl8NHLؼ b6s"W>JbbbpPf3͛q]%z)X,W>eO,~f!Vz18s.=:g5.v7˖.F 0 XpF#,B{( ײ8ϙ݇@LB#v=F:8YrGXX81|pl۶ }􁷷7zwy'ɛh'pmnc)2 ^'|Jg 5Ic~߯0o| gb,dAVbئR :o|*mރbСC1l0[fBa~z&L萭ooo,^/<-}n}}}~F_QVYb.BNdhs~/=- sSS^g{M.`Kx$g{p.z_VW׽[7uJ]hOrl{a/>r|W;=۴ &LĞ=x TWנ 0a"o!klzujlz]׸<'U7[P]sqcI aFNUuH;4͎#k{Onbh4#GBee% Q[[P;vtX@61@]qR]nn9)b(O2v boF#ic"B燺P2-5M/_WJ~FZ B%ii+A$Z^kllV h!|ئ@C~ZHBAPϣPQP}j k]\-CSK@3DҍG-CɺN1I\o\^rAb=”W3PbvrD zq H G@.2jUz_Z 1K\A D*5/ŎCQl@jy;o'tαv_36Ib6D"Q@7M0j!CxǶN{ Rp:X`K-lyXbι( !`ا >>a7ZWlOL%ykc>v\}L -P Èrq 2J c\'7 -vx$pS!gXfH*=5: b5))_}+  1 ;O2@wH Ъ2:J= @+p Y kR8;-bϗCI  QF`h)dScζ7@?Sb,YBIB mb ; Ygv+JvV#WpyO,% 1…%{Zet4Qyp)F[PrWY`h}0 wEE9ȑ#(//׭-lv8Ѥîմ{ Q:Ro]$zOLP[[;wGeEthUeؾ}v܁Z 1e$F#<,ÇΝndb=!6\uĀ{ 1ِ1|1bRGb 1W_ R+ ئ B! P[[k0{,=r Fٳf!:l{=\tGgBlL,!7 ,Ļ[>#߾}{\u/ ƕSbIR <[] Irس'={2&  BϞ=w7WM+s`6y M7݌wy $8/r8k ___̘cGM&B@@; _y%jEoC1u Kr7No att ֯łRl~aO{;?e3|f؂cccq|)& k_|Rҥ@SS #ڮ(X/:1...w87((7I g"d6R"\!P"2TB/V]iBNQ^v^P@uu ydhDDD4᜺:ˮ2ҥ *>e$,z]nG`` XW1ȮOZRHnd9\mXz7#f-κL emaOLL ʲr.^p3u$<֥o֮]+VcXt1m` 0zd(fEP=5v.WAzZ"yE9?;9}ʗAm :GXX8aX8c8Hs5vs}z矏 ŗm?f-v%aзo<݌>7DǎlBacC?H|m:ZqQQM IDATcЧo_<8tCoE{!">>AAA>#"I$\ \hDX0P3Ws"PZa)ωVBX,FxzzbQDAA>jkk>}cǎ{ 1"/t rxlD07Wky*frLFh4ߟ7. >?FJg$?]:PyZj]Au2 = }R )P 6$ 1XC|ZuR>#Nzzw5HbEnKI9-  H P᠆W@+!z[͕bLA$Z@+#(wԪV{l}V{+i H \8=/VhC jNAh\HK!<5 fm"ۢ' B LwG[NOK^s[MMT~[DsN_۪۴4>T|/bc'w{a$D֍9h9f䠃9ΛHsQi]c D a4V_${A2'#'s2MMe]!Py|m^d4Ψ1=8שV} =!i=SA$ =ѻ"N@N;2Rl-~NBυ  1F .S+N+U=5f@rsH/)A$W@͍,W0 ZٓsJ9>,oO'oY(+QDtaL&L&2)\{$*=- su)?=];JJmm->>zJĄlFvv6ƍ///n Ҩov / FlFARGZFᳵA=DVqiiɟ< WZ8!ܛ={2ѳg 8F*?h X, .7%XnذqzꍐfD8K.!=m-n}555~[;\l^} صs\y ţs6mW]]{f<E_YYŋ'  ƌ+ D/5/'o=zDU>Wf7lu{?Ԋz/z?|ž c0 WX0`6mڄrzL&';>^~E̛jKbs[y啗/9ر#Y]ϭsrw&O>Wb;plܸ8qv5|}nݕe\gu().0=ҥ:*m7rryUs]jgc-'f|%}W*8u*qqB<<<?,ٌgWAhh( fH7lEtt <Cj3f/AXX;a@ɹs(//CTT4ټBm}|m;bbba0|J߇lÇ?ADD VXqcﱉ1|6zz%iKICFH#peeZQg ((fxyC ; pز-d؏aɷwDMwyD-,\ŋ6...u$ײ|= _M*Ȉև7jjj0 t 9Ϟ@dgql Kq"uxͷX& σbU^`` EukO>`QObcjEƎmJ@R\]!s0ͼBl6#//QQѢ=|"//SSDAA 77g9{V 8y&̍6πPy'ޏ La!*++p?vx,[8<-\cƈWrZ5E,1#Hii)+!& Gvv6, 8p""#﯊g`cB?9$% cFr02ov<)e˖`ӦͶB<8tCoE{!"<}>` APP͛O=Ze TӠhfTL]bFV AС˯Go>45|ף9pHSV^uغ,d2a'p*O۷ǜǏ?E{-s ؿ?Eť~$fkPy^^^Xj5~??r13ߟ/֯߈ܼ QOb̘X-[< J!nB^^^0a"JΕ"##;w޽{sg@ɹRL07^h8ۏ<|m{o%uRTG4s힞9rO!a0bbQ$2zx2OL]\@ʵjރ䊴DŽe4G^1h`We%$:x 0jQ-w,T55Em& 1ЌF`^VC_n$, 1F"9x,_Zԥ\CBxe1jbF>+yasNu萜 uh %hĨX@YYv~&O ~T #''bʒCf[[B]B}lTgW}\auyh1p  ~1PcθK131iitW^Ė%Nu)}|Xhl;|ZõҶa'+7a#&hk|1_Sz/MMJ?ѥ=k9ROOKT͟>ZuYpk ܾ\< cA6W0YGuu5@W^ @eeołŋ <<!!Xd)>x;kf1mb >>/Y(cɒk0_kBW\sM'CWa4QakAi$Km (`HH փ5;x°6-~{ ̜1~QQcDpꙈ+ꦦ&;q}x{{ᵲ29lEGa8eHioSSm{{qc \=DZp 5V40 @XX|rC`_xx8߷y p*OY3EǍ}oqw߳kۡBߧOFpp0k=Bm w(٘… cS"#0kV ٳ@Z<j=-h=Ӹ2@`[,-:e2ۇKjq]?/ 77y/x>}&[{cXYL6ai%QRrX5}m`3PxX3Gq]p*/(qOhi!]ksVBGiPωX.=n lw)ۯD$tǬGRpmi3.D]VxKqG2_??yl.^M>:40 aaa{d2ٖzxx{bE^?3UqmZ9ĴMCgn'yZga\1|prmڵEJ#HIy=<={6fϞpUaHYϙGuK_+:t%KxRݽ;m}/]3ĦFRպ-zZ1yZqQFE3Zu|-FuH٦ZI=b*ȋ=ƍS>H}A/0x):Zsz.$$ 'xi$ZwteVB-oބtJ=NDp q@k2Vsy^um-mwXZHG9(q3sF=Zw+uUҾ XvyCA-ʀi!4E@Gr (π?j^2Zγ u]%^πhyM7>!w}ظ8-adh.3sSSmE zKU[!WjW-`0໽RgàAI zuv]l!sf@mHR>3Yh  < TĀ FhfbU#Z/9q^4Zo',EphLtHN֭*kZg R %ӝF|m2R @L߹ " Ā{l߫dGDkNyԪuz;Vj{ B hi?|?Z̥9-z,ڱP6=A 46^R=msҪ\%Wy ܛƏbPg$Z"TBJK[= *_+ղ<4ܤ͋(QG$r̛ۨZ(%&7zΝ#Ы56*z_8-EJ6R"t5Q^QjK=vm$H R6qtKK1r;8JAA>LhcعS]P__l6c2jJ+6'-~n&02zw Zrz@ ߆L4mP@2sbcb}yX`!`2lZƙ3۷ڴi6/-~n 14 Z#溌^{|P@㏇1gc nfl_t ˖.AׄxDFbɨ(g-Kj,\ ]M7"C`6ֲ#C؈NasN<=p"ލg 'v}A7\_bk{ee̙k g9V1}GbY;x@{jZA/',ڭP7p֭OGAA>+xxxx_}co//~v?Fs7nX'N`W_GptaL&Ng]u)0ףֽ;VX&X>6g6>ܼs;v̡_p}5().%%HO_k#1Jr%{b˴+GJbbĖ'հk1=@^ny5j$ti9/YtO/gf{~zBV &&X|%m۶]-^r˭͛+Xm%Ρ QQHK[ |lq?’pX s૏bU23minKOKTLLDzZo__X-ι_(|g\z.r;~3"9vq|XG`X,(--6a]Hz bASSkYB#:&fحNnC?cժK5 >1m}6Mо},_#Ge+M s9 s _}|mudS&0J6K775~>2rr0iD(~ŔHy>l3rrA'%ݣ9\mˣÇ\.`.FDDDOڮŽOwSkD s#PXXU}ڴ[燚jtM,>o6|wNBtpisWgV^+ri_|5sk"֫Rѫq/ZdT3%4EP@ݏ}G]].^/[<5Irss1{V kYB; ǙBTVV`|}}'Uh۶Q\\=0ROӦ=?Ngnl0`kclbRf1 IDATXpF+lj#137vsjhR<-Ih9( a5!}?AƋ/ޟ={`Č#9ykYBM}]:wСύa{oc6a刋>}:WP[LALt',[6m }S > ¼y9>;=0laȅ]SɊXHLrؑZ#b6UZ >V[b }]ӾQ2t&MBhvgͼrGZ%k\vicf=>l+C@Z + 4BT2u.WibWg&T;Z.wsB7jVA|FL -4y ll)BPKyb tfEAbņ>#'`LL5bZMg_ω\0Qڧb=$/z H 4sFB?b6q6bZy[OFW" hX3،cX 5k4,Q-rM&A$Zw@* 8bA:jàtHeJ?{CG_c6v^CR>>PyI]" $;pU|9y{µ@m\99__:)hP!ᮐE 85j'8X6;Q-T3$@ U1AgT^-mb|ldDJ\2Bauyh1pࠫs&1FBOJ{-)P` %7b$$.+7=-M3) yH%YYHI!NV6o~ `!$ w!;>K ^V-A 롦@ko W,$Ena^n.6 DB4n*((fł1bQP^#Z)q|^[* Ɋ2P-%[3 c:WHĀ g$!KKOKorD#,ZjC!I_Ol|q9.UԢl5R'Z'U&t($_s7P[lȭSʔ 0 !rax0,dvZsdT3M@i߱M kN򅀅U+WMMfC\ulU"%@|jI -vXT}jgY$JX, Uhhhp^v\Xp_} sDvѣR3طo;͒<4r~#=$Z BΖ`5b"{Px5ϑ IƃkC?AA6l֯'￟05~;ڶmeٓZl{=v0pFhԩSp֎uiX,X }? CCCXィ&&.^FG@mЈdgg诿H6۷m|DZcԄ׿?؈~?6mڠO>M D$b W=jn={Ꮣ'!!dkG` Xl F#F#.\`@^^~#@kwhjjCBB#075?\9w3e@;gO!Qhc$1D-۵lB>Z#Å6X]Ε= nݯCIq :\UǏcasCcCrl?6m ~0714h,53Ќ<B/5pzZ:$'(FOC_M2rrP)io6N{d ck&9wTyaT ^^^(+d2 &ZaP[[PTԢ >>~AvmQSsI,{, $$,]Ю]{\XSq|١K1[7x{{[n;G2ۏڍcb}0 -ڵE_jmй.]Bxxj7y/ʳ{>^ H 4cA %-YaC] Gioo/ą^6`MM^0 PYY _UVVFQRYֿ;t0|[tt ,G?+jqS5b1z덲JxcڴѱcǫsFUU߄E~PghF57MĠK^+>@O!@״obT, ;?݁I(t/y7)SQS#|`7<;P[W٢_`:GEe ư+M7ٗ / v}vB6m/7//O0ͨA]]=k| zóÕhK) {S^G?߿7\o?MI3R=Foӛn >5|efbnjbDH-@JGzBA *+敝9s53 JRVY\m,*.qͨٗSU]jEuی+U5u|2 *߽)(w<_#sEb\̕T @ΨZLRbf$6Jۉ (f  4NA v\#-g-6XRLRi#?$H A>8}~JGlVbgL$!g tا H F3mt+fX PkD1bFq!kBRv#K`&nf3r3iii ! I)f Bl| MJޙPAd@Z ^%D@(+`":b0lX= =lHIU=% کlm5%-> @\%|PnŖ o!Cntuk=ؼ\yFb Ab]qM&2 T^Y8B4qq4hbZ iiW%qWl"!,LM|&nWZ)C !1ЊKw,dPŎn$'hQMe(H @H$D}TѓGH;l5IHh AbK5ZV/1R+w'F!aD"  -UmUL   0p 13%n\͔LG 4!A k% W Vo , $H -V]v( BJ @H ZJA t4RGΛ)'Z@H H$YC1 B Vn٤.  y t w>A:b $ AB( 1@ʡBm?AѺ!ANP!Ч BG( AD+ A $I]@A A } t Ab( 1@ʡBm?AѺ!ANP!Ч BG( AD+ wPV^P7A( ;?݁IPg&  Vz B!;?݁ΝyϡBmOwP/A($sg uuuhllm ]Q?미; /// :G,O> Ç'q}̙30zO1b|||W^yEy=p?.\+/֤m;%^}E~"ռ=pS1`WFE\>0 '1V!i=ڷoo{oׯN: 8SN,K^x<pQbԩ0aL3g`߾}8tP۷j׿?=~Yţ>>gj|ջ7:w/$~b`%a:e2a0XMbaga Fk˗l秥Ek)23OGUeWUYO/@BxDa XDl6#"<!󣮉ÇѷOo֡ ޙIQ{wzl*(PԨר7&W qK|&&uWsQbF qDeaDy:5NYzf~ϧ?KuUMQ;9AccwoƛEևjw):L&x,]ӧO1h \veRUlC~0l0xx믿^z)N9F@T>}P5mXlذmXL>x2~mlڴtK|l YDHN wSマ^{㦛nHܷyO>4VR ]t."|r<> {zol6|ԩ={6PWWYf#H_+**,X?~oѢEXnF}>6meBW1b$SQճ'vu?xG0rR6+ny^|,٩}Ck}3f矏z~}xE#ࠃkG-~ƙi?1ZBRGF;ߘ摷y:th|+ݍW^yL6o<ԓqP<#Gi~k׮E_>}5BओNƂCmm-jkk`8x sk8J)Ԭݐ5k7p[)(5УGL8ws=vV^j?9眓R_hN=Ts=u]cgwޘ9s& Aa̙x;u}3VF}:\u{ǝr81a2x&}@,]~{> .… hX@H^| ;찣p͑L4kwJ),Z3fLQG!ʕa̘1>8p V cǎu~n a~b1oS~PEQLWu: :5P l>0LG}}\veJ#O~b'4[w mp u8d`fsn'y$^nl+R*,[ ^xϙi裏!RfCouעf-֭[kB_Qx=\{Xr%6m5\֧vX|k>/o~(m. T׬e63ヒ Ɖ'>~x̚5 7nD]]nV?a̙3fgq1cjkkQWW/G}tڅGUU=q9==W_s-|A̛7{,<sObŊvW^BROjjbر8J~8ܳ'`ѸY<o>`ԣŖ-8詸 9\L=O3̜y=hai4>O_|O>]w~|0s(pA ;tb#8pgbҥd?p%`֬YPJaýޛbO>RJ> !~z?яP]]=_}STѪr; 0&e*7 u*y.TD0rN;N\xOr*1aOJ;Dy Iي\GJI.TD%roOsO̽N[;8O ӝ0& :7ƍ+:Rb`˖/x@}}Z{ -ZQG".%;jlI}|X@HV ,\%!Έeu@D5 "MhPҡP Vvn {*CPBi‐  i+Ru1Pf|A&wY@$8!  i+Q@!|C eB0lX@H(H z5C)B@jsF2)!d+ U^ 9? *#TF@9G裇NE(tP1uqpi?'t4P_B1@Z vaӂa B %QPV* :~GB:N@ŋ;h-}*iMf/Q2t&3f^RvfI W!Bᐲwh= GS4vnSE :^"QLXW=oRҹF_, $/'MBŋ ā3 q![iJTF !i -Ak] GREHJGc7#*Cqsb S XST HAT> :tq@Hg @pJݦB[j &CQ@wi*|Yݭ>rt"؁P tqAP9׷?7on+F݀~MR\׿F$.&\Z7F͛) |C 6 ! h]X5+UhrV!l/q@H'b -5I DAq~,"4_<y+0 B(enKBgxsN  @7p0U#"!0_X(3'KS[D=aJpPQF@=Pɠ13Z#w-,QB(: 3"P3X߾N]]FY˛t X@Hq+p\=8%%Bv' Hx<8)־{ =9Ay)8@q[`!-87?ǙvVRʸbb@fC%pR]]IMNܴ ڀ6mJ=6FDG[PJ,:TWcѰa a=w9*+q{9tfX@H( p-HrlwZhg@!W'`rƍ"xfJHH5٥BrA)Wf/4JqK!b`N@<1%e`sM Ā'"oGqkj8x͚8fE88:[D}Ы"vmt%X@H(;rJ nf!=D8>0S7lSFT?bqᘲf kBݜge%Ym7oHb\H;'%{ iaCP)w=/ixB(:( *$xj@|{zga558 7Wl2i*@,agJj&^?ğG]8lTWօ r/B yP/#4 {lSV$B1@KXB kH6br%RȑƊJ%RRov-ia{/'@ +}g<-BS1P,m0 . &P,E`GRʜ`}HJ)hA >樣0j5r-Yh C|=*oCxѾy) :8])!b<\EiE!0O^ѠB(%yg=jTbϸ@kС8d(O) q_wމ^t]+<+އ4!b++* n' x%u/X#ށ='콱c{,[y>MRbQ3qbo'~ 0ۜ.@[j`h+ s WoתL_ѣȝ#ak;jeCn#s˥2N%Krb z#@zy@ŋB(HuAn<| %674 dx?Ɓw]yQB(H-EfS rMx.@u+1fL;RR)ϵ8h4}4`^ )]bS iGyw;봁-00;))l__Xc]q|!RboW4,Xzokܛ !h`@pL-LE_߆/v<]4⟆8xghlX:>G*YyP߼j#UVGh: O -qocW 8%`E^[@D")ɏ^S(E,3__Rg]q4` B)k^y9y;wSxB1Ѝzͱ e=4hOPB? H[QLY,g$1=+.pC|"> y΀\!60$ICH$܊qRwމ aɓQ R;z/R B1Ѝuw+:2/ D@mK p  { "yv/+]~Xj NH;P)B!Ph C4zvwB(H;^HpʔO1Gk-c-ꪄ0GW\tlΆCEF(]+J8Ѣwmed !`\(\N`UW "'hj :W آxsDiU=}E f΋/ϤI[[,خBFa= i r D/\ZBzKĢJ k*i_O\YDHx-ŋ1L mo &(vN!L%̵>^qGz{V^tQS:t\58wҤM)z9}Z![0G>p4"3{FôZqoHv@4,Zl;{ln,$$P -4wO:+C" W 㑱+MZЕ60("RMGj@:FZa*)""DCC=1{! \A+` 9p0f#sj!s~/Sp"B);AӀ߳'zVTL HZ(|fHf!blSa ;."T՝!ri_Yǭz[)Tڴ?W~1/y( (Mi+RBC64!b0pynk۲c <(܋%m} bu p/!iЮ;ė& tE8( g*B /LFiZ"JY +` ø^t!tHYwb嗧:XAGiC~ j,y _NQ D %`_B)k(`區]kC0 i(<.,$P]p RA;Ȼ6:u` dQ 8 t5&褄JFRMȷݕRV{\s~ZMCB | {y/A"=tBR@lgkA`ΡOyHݟ@H(!h(&0T' EHkuFUR~@HH)!}?OqTK!y=v κCiTK9\s8  D) asABHWiNʗMM =Z.i~ѹ)+'SH"u.T!P fܜ9q@F,tLSs{@JP HNRR f=tA-Lo7!4A' ++&S\!rJ+NZ!\3-xLDЂ@ 0E3@:76Ʃ]7`A3j @fA-),900ZvysB(H`;@}6Ќ9S{G]\hߝ[p dZJ+9J9'\ChWl7!4AgwЯgϸv 4,PJ%gXS t*<'XEjQ, h 54`s}=~;FB(HY 㑱jTiĺ(<u :W$uMz]z)zVT- <6@$U@0Hq+5+nV?w.B(H0xL3b kEVLH-\_ B(ȶa_ _Ϟ-"Qj@vYKRFzO!|wsmA8P?7.QB!0Qԁ+3Bx뭼x !B!P B!bB!B B!B(!B1@!B!P B!bB!B B!B(!B1@!B!P B!bB!B B!B(!B1@!ZlaIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_dragvertex_after.png000066400000000000000000000354301466047437300262070ustar00rootroot00000000000000PNG  IHDRV'k3 bKGD pHYs  tIME 14si IDATx{eݘ H&L2KnK2uYeQVV7ª=`q,QV=xYu|EK Ldtw?SS]]U]U<<3ӗzV[oe|?33 ~:oW~tRT?d(ʔLS#bAtZi*eT,A y!7N:Y'|2r&%C2 ej㣏4Mp2 CE(TAzU-)\!V0MS@3{5Xjڴ-)zv ']2 C!&0 *-Va3UT,X>?eybrx9X!^ͿA<;) 𽁌Jh UrիW'P. e]/P2hLƱ:::|ٯqI҆ &{*8묳&źa}W*jݯ%4 acO =¶crUj[2])\KbKq rJ n*S^cɇztx*^\эk},>󄉓ayPgt!tnȠdXx*Ş={^~OOO(U-[} 6X,xR6lP:X&zHI5ws²[1',r)rk[foGwLj]XFY<݂jm3.+KR. HR:46c,.%/Hw?B:nIJ3,H S,[]*ox8Պ4M;SwkqU>{K7Q,0:|^N[[ܨU;{FdexMG\ϲb_LڏY]O̯beEԍ;_Yn~bٶ9.b8Kw_4:Z.kR,|zjTRfilweH #~dI5U1H_ S,8)U͜`XfLt[r+Eh>42e7aTR>VbՍnƔs>׌3Rzm,[}J2VxHT DZruȼ96HyX 1U~m7齞miuvvFK]Wۓx߶an~!@qXzg4#y%_3ޤKQ"R~߾rXr9uC:+ߵ?|^PnFNC-KSJZ>;uK%lue_U)6\Ӟ|?ԑk̒iӦ|.BGKy9vy{!*\ݺ>+jmZv^s׮/LZphqi.&}hj??0roO6oS@n5uWdc?OOoݟ ^˘mCtTXTM恢ԋOuKw_oy똼cٶ]3ՕѾЎLIRuilϘawJ*h" DJJ9SR^zKn0IRWy% LflY-w~K?)%G-,+F*gͪqTF nt ?=Cz5Gwҳ[y}E2+o5aPRP,NT,ROC ~=r̟7[Ra3rT/c{r:\I 31ЌTyFMټB znу*τ$[m4$p3@jJKqHqӓ0d6ܞuO$ libJAFiR䡮*2UV-WkUE6qVL&c{ԝs8 ttHcݗ>l ՁK%I~oreop;M=jwa5UeVUH+~y~?f\VK'Iæߖ_m̔UT#]ݶ7b Eն5)QZy[vc~4 fU[׻^!Uc A&Um]/18ݡn[m? tuI7}I4ցKuWXt,v56%Yգ XO,Ưnºg&V& ]pmyYeU;yfyZmbS׋wʊ%J}y4-#UBGjgMYn+KtX*O7oJzݱk]ƚn>M4̵e\(UR2Be/[>bU'`̫{-G43&Xk7`+'>3E6?;))KUPuYkqD1mмWnx] fcwVfdX5:?*A"\^?P,֎ϗ't;q˨ X<611甃;(A6gu=vy=ad\j5կV,\95ALKn!%8m~wZt?Ii4-:0=֪uY=c6U ]پyV//X{~hNz!٪!d+0w\wzԘ R!ԍ/Vך!9=kULzꪏ b|UC}I'LF'22U iŭ^e2nw %7NdlwOybԶQ5b(㫬XFwc1+-rSŘʨx\HQRB*8䃡ݎtWd&f%#H]b-z ϱx/%%G4Z/A@,Ry=bϹgⷑX牳:kBH>$ˋ/3$P2ESiP4dZEtʩ+ehv)bUO2׿$6b##i/(瞭[Ubl%]jTSLԯ~u^!ȅ u)+J5Y)BkK,q=@lެ >r^/}nN|]]ڥY~Ԛ5w= }odr9wLfl]]ڥYlެ_p f&լ! YTʉJHffrfϚW^yEご57I9s>lmݺC-oAYfsՎ;\\..Lg̙3~V㾽+Dsy٫7ݠ}v dϓ߬'9xڥ\.BqŚi]}ՕYz%%ڥ8DGx^;A^xI;v?~TLt:Hqw?4`7x.b=3ڲe?xs9o&]r%zkƌz7MzᇵqF7MOjukllL_kZbeoG?q}Ԇ .2nX[N<C=M'|2t{9KKKሕiNa$)JO)M>X_ˎW*M޽[tL/Z~6͘1C}}}kuo}KCCCZ`ϟkjk վ}9ر]G/]_|QKNc=QNxc "j06Gk~QG/=J̛}|޽[ŢC=O=ں͡]]]j "T~h믿N;vнOTJt/-W߯S3gv럾5]AO~۶kyzmd2mGo:v />(w^}_F%{]z'uI'?O~1Aiʔ4W_V-9j^)2+_< zۥ޼N6n͘1C_]sպԖϟƴkCDD\<ʐQ2],5оIi} a*n̙+0ŵW0tyW]}z{0 ]Ta5Tћ}~K;kgҤՀER)utt(k֬Y{/I>.2mذZ%uAk_1Ob%O_ܫ/_?E_Vx;lxNTkm}[˷4||z[Vꫯ-V۶͡]]]j>O-8]J-(L*4Jiܹx饗`k~^|I4s9ihYgwJL;wIRm2fΜO}Sz衇&w֯_˗^JGGy>>ڷ}UX}kt_/~q޽]zwiٲev^Z|CzG}x%͚5K}ڼimy=Vm'...6ƪh$:5jzz3VW_u?^/Ð!q&}iYk]]Ֆ-[xQuehVO۟ڷw>訣ҋ//}KZ|y/ZnNO"2s֮]K0 MOG.Ho?o?Y`ᴕg|> /@]~MO{{>鋗ՂSOmѺu78]%%ڥf2A婅=KuUWMfQo}ԧ'}iYkz߾]{3nkTA5`~=\=9s7OӟŢN}{{[93gKO.-ӦdXCDKQdzXJC-X *j(?,8L7t>Ogնmw.^..R,VV}N)ʐrkf[wiڵyz|uwwNQxhhv)xt:Uz6W-2lC:JGuT}ÌUٟz?,:X+u߮>/a/x _jB. %bI{5IDATcV ~ڈ.@%be5^˗V -Z}kFQk,cuO'[Α]2V-Kmh熇ձjU똴rXxl9g/8e@Ԅ&߸xٲnF]zM/Ï8+h9>`C[bEQ/t@[bŊd T'G&r9+VhjYNХ˹[=X@[HU˻[~pR?A_ XIU]bnobD vRucjXD ]~嘪Z#VЖru>.B/BbD vrU*TYw bF$P~JSʬU] wD?惊2+h6nlz7p:V

AW p|r Ҭ$ݼiDCx L) .UP!X1S~<Bj DIBU:@X VHU$\.Rܼi+@ >RU:GFwʩQʋ0|fw݀U288*2n.'CT+Id\%UU240l6+HQq\TZFvxD*Ī̈́YlHU5$s\"UUIU҄r}\dZdY E SB%$2F"@*(a`; VT%z hT!< l UWH]ZQ.[*V##|V_e *V^ȈJiuYPilƔPWm,-=< N: 4NR~+n/VָM>!d t,զSf=kp5XNǪU5ZDr^'n˫NVʏ;ڳ]_X75A@*@b%U7oj֥^@ikpU*UQP/)lo5Z cTA$ڮ##; Ue +@xBF!OUkd )BeIDM*Y'Oo8KU܄Z܌ ?)Cl6[u>2*8 ]X!UPK*}\V ;TTN VHU]$;0\+HU8X7T)j)B[gQjq"UT%.+V9g!>+ )&umݤjpp}b)UQyX'^Ng|Xۖ HUufr"qVqk=* 9jDfX^!V“oOfOzPR 0-b!,NdwMKZ-3~0-b-<GU$Zm=T4JPջ.fD'V4= HUxBJ^!VIRE8=%@+TDWDje05A\e-!g{#`T'TQWD@Z5UARoP- 7*3jY"K^!VT9ج\ 9'vgs Q✑ ku^%\:GF|D[jR7RRn`2?X]l1CUYgIQ=rsþ|\},2¨;X~_;lv kkc Z]Uaugš{fgh``ҁ㔡[yYFix8˙.gjUO'XEnp(]?q.Ǟ/ۯ*. Xʷ׋;V͛69 kX Hq?ZG$ ƚ*Q^.3= V-->=RwLgWpZo *U"dqMP؎X!S.RŲ 9FSmPDd|TFݝU^!XrebqFkފqVq{ *KBb 'XC꽈T%ΐBB|$UU9/I Ue6*64+rJ\jƀK/~) [8 UB^u6F~!=t "V&"WSˣdG݁~/сv0OaJD LBW sHd8hTU)H L`0E Ju25{{mJZ%Ls'FJ B3AIJMvbv3$.j|U+ Q$(R:-.iV@(d+QIV KUBժQlx rX%FpZ)^^e);F r#FN([/WB+*\.e9H P냑:I4*7Wr^|䍟uWbUv;'%h嶃 V/.U^.K^r'.@HƶDڜVg 5ŊX#Wߜ+H\Q/$QA::K]Z!Ɉ.UNjŊd0:ygCeɃ]qv<"V ̬U"&Y*WRA_l6 A*IR&YCVB ZE6՚Dt?D@d(WA$NYlH LˀTQ/Đff=q,';<<VU+˗ѶuSuV펥/Vtt_ t˩{AӤT+W*,:5NJv+'AQwI=fkuM䆇C龈s9Vr MeYY Uo̵Fש]wzX @ ^ RlI!ѕEW3UJS<.WD7a;HUD\ ZjL3C\JjE'jECde1RUU#'j99mVbA2-]kMݨXA+X1@XƤq$@4 D'RR)Q:I V\Od)C\y+`%){d*x!r%iR* agV!V@'ܨwӵF rU5uy^Hb18U C(pQV8 ֚e62dZOXsSrVBZ=wh` `E-{<ܼiSyb(T!VCUk5 I8jG* jA漭*H`Z@ R P,\B*%l =BO XqDRVo Q+^\8+ Y{EVzAno?D**fz ϶F˕XX$EN ]1+Fd8g.vȂ!UAzA\= P\52y^+N걇VH@Ŵ G sdėAAeYu&##*WSe9W}/QV6/񃤕uʖz?tWrN7oڤA.-\'/Գ>K;A#m*;}74fȊqNcB8ɶÉ6㭂 xI  XrpAHUT+0@ɰNAu1  5rHbƂ.E DC8O1D2+$+'U\X UU i.0*ITD_Q0ܦĪKJJIl4q-h\Y#|6!lY,K5ydc_d5KUD~2wz=N8Ԣ,ONvOX V{*` Yq8)ȝ+$+*YI!*" ud ʆ@+@u[- H\I6 ^Ode{\yT$l6R}X%HbzЌ!UAC cPɪdE)6/"T\5-吭d@,ihlQ{2w XQ^!UDThΝ5B`fڻreb+IH8 VԲWb1kee#WQ IV)C4XfY3~5I*urM4XjܲKnX9geꉏ!ZZA t& 9zQ)hN t' 07*YqSꑫzgR 䪑* .fR  (jD* Wj6^SKD!bdUb]wDIr"[X&XvɲVu( + Mf WN"T!V VdY#UGb|Ua+dUv U!yX @R + @@+ +  @L/|t?P}HZ9Pw s# YǤ:QuW7d|fjEOhѢK -]شKSO+/ .ZTn{h g1/  %"@+ j<nIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_dragvertex_before.png000066400000000000000000000357621466047437300263600ustar00rootroot00000000000000PNG  IHDRR'bحbKGD pHYs  tIME /(2?ӏ IDATx}՝q"(6qMܸqhd $xk"5J&^dLuM"n^@tP ڰNwz{^Tթy恙:uw>;N)+5: ~ M B!H !Bܑ2PU$I(@Xi^ziS ;`Xp<BGPEA*mUUqiAQ"PrD}rɒ@v׽%LB!Dң*N8a*ZGл>$jQ?O[G"B"uiut E:|EQXBHIzyn>B!bԞM>W+[Aa?a yP0 cǠ0X@" mho|~PI@UT$@T EAPnYZ#TP(-L²WEQ$  EUH&ʩD*<=we*vƠm,Z>v Ə ơ}\+beaYD϶Q`FXR|V,mHLᴏt"_PH&R8}G׷ #FL(bD"|>@bbNH߽L%Q,fTTL&m%(*ґqZe(@"LΩ"S,EUUE# jA@;#=EMV@\GS&O@>Ǟ%=cH$K.]Z.KkM, 1TJzi_)g2,m8Uze)(v8]^`66n4jQņ7W5H@-P@-M oPm2^XeHu[Ďv=zT{>G>Ďv,LC &vWD m,Z;_2\3<ϗzxR) U_ZSA`S&c,fO 0v6oRVnY89X/,KbSQH_~$ O h{%S2 ulhl6kZMMMbbx&v@vej`:[) Z9}^nI]C"l)+F]ю{J,,LD6vD^2׋ L+eihǔ'6La߹b/k Sz"TケWx@%ie]ejҥ BE(p?!KRu$~Wׯ/upyUuַD"Tꏏve ܰEXt8-ɔ:7e0p8@icxs]y^Dh'Bu/oS?ډ7T*sf[G* XܕTa)MeJ^luV/ue/K>]Zj ƃv{*}NgCr`;VhkK5JXYD1#,^UYTUx'N(^FvW_{ͻʢDID5r:eۯ,Z^BU+G{lpUd WFpdyRR7/%ڲTƴRz-(ʰr>6CI9@9MCٸm|[zFƟ۶?-+ `՜3 [c'BtYHQ@%6YT\S.2U AW/zQ"dYd9F:3pyqm,#Q{fS4@).Eڲwyx'qr=Wv}ʢ nhGS>&Qˀ8Fx)kIumu,Z&M_)Z)`ytToC\gm?$hnnV=;|'{^]p l5,bXYj.T<ep;'KmĪjU .Fdʨ^sf[ WS1A#e7^:c挍U=&q$D֭7Q.W{jRNs g=bA~l(4q)8(`Fo&S:˓>JxEQ8i,6U,8*CM&JE˔6Jy6@?{8֜Aoj8 huA?,&Sc6hh;?\I8E 03eu]mv,>8a2ݗmoآUIdJ-'Uggj;hXŰsr̦+0ڿϘ~vgDNnJYS3 ,KSS FO uM>C>跴8:b&PZكEZpY2ezt9)K^<),!f !QvŪ"D,voEKVD"ڝc&7IBI8j@݁HHl穽FNM«'[Vݐ~e\l?_sa(s =P^b _@lU/U2D>_dI]+Aedi+ɹzg~L#7(,he1Q*(Ȕ6Wt_(lзkȴ,A0xm8Q=Wu2B9HyjĨwh>xn~FʺWMjW{%e0:@>7T/F60aްcJm Fe{Y7e#Sγ@3{z*& Ved(@Gd,Kn=d^R) 4~>ˑuoRvLS{v*f+10D-jX :u-CYV^K(f #;JՖXtiWx1>u) Uy@/ozqO7. {jΝW$j$ĵSU/fnV/,K5fOy%3FeInBَQ=Ţ/㣴 b޼y8餓pmĽVu>=ȣ/bʰ^t-z)?勔͎~9})I@IJqى{j;}dX4SXe,~]ŧWĸcx2*~3rFAEk'; w8o/m, ت^*=!>H٬,J$NԐL$QDS{vaz߰pډSe ϰe1,cTYO._r$ ?>7|FbLyve ?+*PYj_YQ(TuBx52y CqɻņEJk:ng IvRzq,*N U-j&B!8S{B!a8_t?@vu`<B_jYp  NB!$T"eB!6mbB!ukB!čH7kB!& V!BEB!"E!B"B0|U<1;v"<`XLE ǎ? P +)j (JP(I`)dA@UTH$PPR@"(( Tmc*0s %¸$Ky(RnZp+^|%0 Z}}cǟ+—X^ j14!KqIXxx2M3F"҂gٳgc;v`WE_s_c? Oy,'\#C?W]A*CZ>mm54a\b\"KRv؁˗`^CD<ڴL+pɲYøĸDd)HE(B'XtLj1!ky,I9KyqW,Z'X[.K1cføĸDd)ށc 7vDa׮װl{Gſa.}ϽhjjvVqѣOM_طoey2.%IrŘcqb k'ax1886dA~'܌1c .@ԧ>c=cǎŲe˰w^pcܸq3f n  < wf?.">~O_ /?ٓny~vxZP(088bhd2;n ޿`wbs>Hٻo [FO2('L&<ن?nǛu}+믿;wSNE]TUW]7| F/ش<<^xl޼۶mo[<#>jzufxp"W_k:֯_7G1Kd/`zXpRx%%%oDJUK}H$U?Ţ1>2bQšCkƴSk\V58<Ѓ81}>񉳰u6*&vG>Ďho;QQG?9>ϣ ׿-[T>ꩧ|GFGG. yGӃSbʔ)Xj֮]+0ҿ ֭[ph{93qA˿|s3c֭9-NjԋKFC6{V݂sfa /áCP,<<mccs'e_Dжnۊ7 {}{oG"׮s7neU;{{Iӊ~!\]_L2^ߍT*>vՠOm\o|,ˆ#*9r<:,g… M T>?ꫯ `˖-~jywqV~?l6+ֲl?Wo~nyc;7߼ `?\㏟*|Eyťuc??xSL(7o?wqWbL>ma\b\b\r[#JUm$H`„ U}w0uN8zJNϟ|ҥK/c-$Sdc7};v^Ǯѿ-10.1.1. #UTuO~;n O@Q[n9|juiki0v܉3gO?[{K/71k,Ut=WƲeꫯb̘1'?Y4u]bN;*X=z]~G?QE\z饸K}Kc„L04^q+X|>KbԨQ/B{x?ޤ&DO;؍W\aٲ o~̜9W\ika\b\b\r^cV]77wu;GVŢD"z5Նۋ_ .\`ջj5rH}ݸ oL$-̘={6}+V`ԨQ8sqQ\sUs>t:l届KqC+V^+V^5D"vWטøĸĸ<"T @UK(z)'|2XvqQ}x70jH,0.1.%Yc)R`>Drxx~:x|_oݻļO1y-\a\b\"KӉwǗjX|)8'OAkka*a\<]{DJ |_\wf¬Y, 5Ta\b\"KRM>o"==,N69md*a\<"ݽ|dz<|i(dz'NŬ X2qq0.R/>'FL1/fϞk3f~w*S [y,I!Fh ڵ?bex ԓݯU@%¸$KynjM~&IB!6I !Bak;v!`D%.Ytk `*SKY⒥HijEEB/̘1Q)|v;_3պWshZ}vO烷۹`u?gFDFp!D6!чrB!DWΟWoۆku(EB!qFW[ht밂]{B-r$K`FB!JLSz-Zкf!D~(RBD]?K.*qF\r% EB!(]h|A|AeHB!$tewc~HB!EB!SqvL)B!BwƍXb+Vp]F!BB'SNק=W;&J{-)B!JܮH(3B!$2%R´uR!BHB!DD%:(RB۶5{MKx^\ooۡHB!$r\9/BH"BH$q;I ? Bq EB!"E!b͢E=1RD8=]]Ctu X)NY$09"%J[7l@&AOWW\Bb+Qkoǚ+BEHPUoNpjS:F&A3tuUdJNWƺTŜyםmsQDm;ŇJ χpm0e*ۿlX:m(S%[m4۩7I(mGj׳fv˱ Ʉrx>c;vN2ew#EI]~KEH4ѺN9niH,B#Qz2L%;E") -7)J+D"$\U+TNDI Tz ꄄfƍ-N@!tR8:!fL(T B&ʳgpuB gvG@RHE*f5ͬ!p:D5۷#N33E"QIxY*B' ]PWDADJ/U6` EPB"URJBŁ"E(Q!*J!Tԅ1؝Wj1NOsDm;Nd*|*0~*m*L&kIc0Bwev%~fɋI%kJ;6Ql8ui'PqB(Ye$P {L0bmӕ/QjE i(R# Cfi7\\e1x(BOK%vQ0/v7e*HHJe%'" an )T2Ka* ,J@o D'P2 S)DN~ DNQ.5bv"EJeI>MH093î>#C!W'6GJ]\W"EjǞ.쒾%E2D(Q˩ݤ{Fe*l2L"4l\/¢./N_/ oSyg:-KWλ]K˖)'°[OWe VwGCOƳO]nؖ/גV4^_rd؎)UQTU]uQjӿ\Y_/foZko7yk.L_>/NG;sXρA @~zƧR-DNVDQƈ kmzSN<5Y*돍#B/25^KlS"g2*}7v'y.!JlDJWdkoGW(RsJOeʖP zԎ[3H*2A4Qc5 (Ա).ԃ(ul DvZ/n˴;E$mHR孴Px%*|e^L?ׄ" (f%zD>+M2->H:Be$ R(%FdmxM$dתXy,G5ҨHUDH Eg2d f(R)BU/sUB$FdGJ˜yNQ(S.@tfPks__CﻋlhN&%y(3i JHjv,L"Y=̠״0Ed^Ԡ_nG0E*%4STo Uğa*# -P&Yʀ 4 ɔq'T~ P6}-VmTDQ؈, oP%ۀkbmK?$]}Hd:< ]g2\{mކTdPt1:Fڶ2MR^-+RG-2qz uWRHw7Tj%Aꪼ?0󡒍PmyQf*ɳFe]4Y/z;^a6{ّ\^dFc~>YYs6*ص"d/e)Tn<+17V<=HѺIt:}-n.$P'Es;lRɱQDTD(a= VJ<ˮRQZ)$,2eJD#迧ve a#D!EPV>~ncE!#7Q.QvEP/Gvv!lGXH4%E*f!0$!NdJ?^B(R$"26EL@He*Y¬T|"E<(ed*fB5g&dAdWl%$nET3+8I!q) zp[ىL&܄%{d*Je&U=]] a6E&Ht'x.Teʋ]&i0Re|z.!i(Q >IdlT^.NB :,3"EF>L92#q)lx He*b6*S"E$("q*ٳSQҋlKAdx/`fP'ĉLȱ%) L\R=8OB%Sv"E P*B"Sl# a9N<(RD'{]=]]d2@@B&TA6~lx)Q3DfSy]dY0:!e٥G 3R1) fL/DE( (D=)J@r_>ϠNB@t^-˥)R8wADM8 I璢H9a}zr=)]Τd2LO>4:PMTdKW{.ϩP]KsZoW2"v2U1Bض:;}ׯ-!QߎaUwzݵtr"u"g,v~ϫ}])?(F>}+l;^<ً5kՐ;"2(c`nK_uWo[NluN'kIHpa6\ƣgP'aGDHHP?PDPn҇"EB&Ad $2qw"EKTF6bT /%P"+PnfTc2ڨT1KEd4 ҫE ED' U#2L_B$a(zqߵG4~E)iH)Sz kKY`)J0a6$xnݰ2e*TQNf(QD:1&2Twa6"_P ǚBEdڮ>eWdQ(RDF묉2%ɘb֑IVvv.;EE*fWf})S2g],QXIGtuZdN- koLd*3(yaV*cMi&Q0#(%*2EQ3>֔(5 Z( "FUDԅGzfA*Jk8!'vM/2wNY`3RlTmլTM"q*N@(Rd 8)JLy=vY E**wGM(,"Eldp;rcJ%b$Te(w)")APJHL"0( T!d˄)QBE!)ZR9^Eq#SG"*T(!=]]ww]FB)b$EHJWY*lPQB%*;UO(Qfs54OQԶ}AMlt[>5um']^_3χplgv!=@ vBF4 '|.TuNQ+dF3%J!o}}DN"JUsyܓoNdLNA!^ȔQ)BJQ4*cF%0.BdK);_^̺ABq"#E!B"BHB!P!B7 d)?IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_edgeturn_after.png000066400000000000000000000103541466047437300256470ustar00rootroot00000000000000PNG  IHDRXbKGD pHYs  tIME'yIDATxsL &kQQD$Ȧ%UwXg7![@(ie-K>nIBܞ}NO?OU>ɼ~VzV AT' A @ @ A A @ @ ⬶>OR[d3l.|P.\%K@I) |bA {B6F@p#>P"җ|nTQm!ӐN@V+RIeDp<Et8HB]?VKx6|g  {X9ASm`[b9 [[[5"WY.ټ_{Mp*a133ŲA&Z"Zy   ŝp*p& AmsliiF# T5$%EpApD$bH'&H@,n_}yD{ NLAs(_L"AZ*:ՒY, ẗ^y{N" E|}j jo/Ր8;TQw/T [>2Xd嗑GxZ*:@U.*u@j)s8EҡK5$.p"^#8?DvujJf\*UCbs541A4/$" Kly>H}dg}Z(nT| M6{w߅:JfS IGX2։LbKAM<@hEƘݷ H''Ţj'޽eho!˅+ՐE-Sl1UкPڇ1w6f{@R48f ukgzƙ]Z3q-VBo}JZzz萒0.;p` *r&l>&a[\=x` k$kp Y.2v fGedwVG5[~;+3 @ K۶m{nxxp8*mGҳGO]&Ep:uoALhC&9kSp X nu-R Y݃'pnEs4fiqZ˚r-ߵ,9|p8j0@Hr[z*5.HCX7Ah$A[&\,_ge-#ssGAteCv1~Ģݖ<㉼;G%dtQ3 +6^W3I ȿloo_-Vh`l,)hce{zz^\v{$S!d/3 tAxgҀ!π x6m%=.#{t}6Dj; Ax^Iu1瀈X|qPC=luY?:Pebr0f,> [ 8 @L;Ҋ6¨?5Hgp hsǏ'WAiiPA~dž&k< ;] :7>2Nsjjzj q8ҖQ۫<u㒈j& =hm=P A@Iww7J&>T`Was2g/sRc#Nao.tq ?D{EuDF/^Lݓ'PQ0ǙLg{P1D:!һ#bZִw}D;yd']"<;`]md2֏U0255tpgON<*Vjju8 ] 'NfjpD!Ʌ-[p[ڍT(F=*z񎟶eU6EHXufՍ㔹֩^+=wwYd{"dE "l!wu)$?G7 yN5J r3NG~É0=J8! A[8 *рR]R=[iZ]'N0b ^qL*Tӓ 6_=NjU4曆ǟߎ^! @6<,( <-Sgg'!e[eՁPi(⚛K]zlsS PidݗM(d,M sssB'vm¬ 0ybt ݋W(OsGӭ$J#r뭷EE%2{{NEe/] MraE'6&/tXa : Niܮq7*q" 333XIAw8dAĩ&CdVtXAn T8\0VGR{u7-Z rז.] ȉW2$ү\B%]Ehˉ`p^[d cXº( "Y_n9HGj@ Aŋ* "CI3HJb Ndeiii92\o X@ TX@QTX@+ 8 @@@@@^@@d/D>!3T A @ @ A A273@[$IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_edgeturn_before.png000066400000000000000000000071301466047437300260060ustar00rootroot00000000000000PNG  IHDRXbKGD pHYs  tIME%wz IDATxoWOtF DIi ~4I!,"H yO:0 6``x%aHm_v{2嚮[U=I-Euu~}:AT @ @ A  @ @ A  @ @ -K^'2ߞ<_[y}Vۛ3kό-=m΢?=[ 1A.,/ӿtt4/Vg:rW uO=|G+Cρ>w̋uȷL/1L>?5E][@tH3=pH, уizxEa$^=G33pM.bC2 x>3EvgE!q 8;K?3p"W?O.(F 8$a!7=fYs-VTH\P9Ôu4b\*@|='pI@,MW?/@^H:^yeY^ɼL;+7>I!95Y߿=95Y߿=95Y?AnM5itb8Ajji "!\!!".hP,pUvf -iXa $fE+tvLhXRK%k\/ &@f $K%7p"=Z٤|L 岹Lw,F}~ބvaɄtH\-˴TR"{-0V@jRB.,lPPay G"1YH$.(p2VVI# 2!qUnRV"iDаTktb(d]}ʿZjVVWטZ2VH\*UE!C⪺JZ-5"V jZQK 46VjT׎M.?\K:Z}4AĊ i6!pYN RL",x:hPp 9Ϭ0Epq @Md~(^KbS*> e^r#=uZ9z IͫlKoP!Tpa=tϞ=KDDWܹEQYߘ;v{mbJ.n~wp#ApWM& M=mN7*~ +1 #WH4mx1VZ zN)/~;e̙3=HIAf; yw_ղVp =SJ'x]eg8N>mjD0-Z% d-3 d~}c20IaʅdXW:urIZUCݲ(ppiеGB:zԚhes[fshDhR0DArIk":>r2! 倨J2r J'Ny"e۔ }\Rh2pAD c4D7}JQaVꖥ biV&GOzҕR ·~2zYp`K٠#Z%Ak 5rQ+`R V8 & ڛb *e:[/N$I?EȽI?rFl8b$t,ĂOoiVp+,awڤ =HX[\~ƐgMu Np4$tfee漄JVܟߵC98Nh#Wx"w#ŋt~Uk 3AGGn8u4K0i_&XU"b:G.]zU tZڽ8tŬbH5[_27)@xpX&ü>66uWFD}a0oR+` JDԦtXQcllhg;ɪ,4Z% soo/$ݣGK(Ǖ9lCI=Η{c|TcEiT,i{n\c-"SXD& » pdؤ_|ux_ WL4Y٤wJnRъ p4eGDG8ݒ^{$ٜZ88}'ad@ڦ7IOOO*& .#^5zzz *CF ६Hj¸mƈ1+qVtGd0Py hbb:n/$PCgם+[lQ8  &9{T!ٽh0E$c#r/Ґwww8ρsaǍW:ta!dZMLL](sYWWYpF5@p9c#jR *ڨ Qv#,Ph]{8H{Pww< p/? j̇BE,o߸q'߿pON" GzCGle:H"M=W&9Jl!q8IE}Mڐb_"KC7khh5ݻ՜~J2r8 %dz_FFF"&{! ֭[5>|H\.*iS#2f~~0$zE2mddrNb1s}RI9HɵvLr+\~wA#YZ䇣]ݻwKqwJ(Z(٣V^8 $d,G/9&?!-#UvB5@DmkAQp?|w>D|֛W-#ǫPvvvM3F޴!ktvv""c7ת>=Ds݉0p'EM@Y tq./{ q]=C <ޝ$ʍ:ܹS TnɀFZwWi[w؁Ӱ!'nd?zA[k@ QUe۷o A:@D'mۆ# +bu!VlݺGJgj2iej[lAApf(E+>b5s! h8 D&yf iZ#V^M6j b^in@qD!;"2.  W; @ @ A @ @ A[aHC IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_faceout_after.png000066400000000000000000000261651466047437300254670ustar00rootroot00000000000000PNG  IHDR|bKGD pHYs  tIME 61TW IDATx}uw< `.1 `mVJC;$6$ɟJTR 6!$^#@/$d@H:tzڻ{gQ睊33ggf}Zk۶͠ը8tT@Q ըFjTH5QըRjTF5*T@QjTF5*T@Q ըFjTH5QըRjTF5*TըRjTF5*Ta~n V \+P@z0Ӷ(8E?Qeyqƞj0:ZPµ42 03QOV_|D6 R)K({81sgvv=PTw`^ǢDrM/>`@#&b1:ъ"LۘjJUطa&uԃ5@c$ KLWr;1jds+f2ȱqDʨkX|>*EJ;+S>%?qWW,n4RPԵF-@Q@8R䶏1|F&+& (JLcӸ+[%f1jTB >*A21 .1ILbAb_(Lds|@κuXhHT@´<: ·@ XK̢\Ib&kL㩩Q*d[)8,(88s0)vDqH 0HW,]8jq vGOgOaR͛ C4k5`1,[Pp09]!d˕yEA!łI;\6`XTL-_0DVK#SEtpnI!1JΏH!8P#os(MMOrS 6ndT)09Jk̹(I7Lb%؈q1LI;ЊcƊ'd-[D= Sp8\Q@!I*2R,Cs%5`#XJe< V ^+&?BKkQ>)QuJu밬f3gԃT AøD`1F%e r+/A)((Hϭ58|/qBqpӦMN۰g,Zf52!\[?Eo3)E}H)Cb*=l$Qa$BQk߆ "rhf,IaM9UBKF8@VY>1aCoK e/MfgT>g>Wo>1pUN%92[pI/ndPLr0eEc$Q`q}/Zw׭*xkbr0D# JXupis/s8$(0/" XP̏HN+5eL(>vp$^=p'&:rpHE+RKi Up  ]Scɥ HDƸCЉԲaW 2;Fc֐ P (\GhW\QFbX x&*` 8y\ }ԏ療 %.,*zܼ9-'y.('IV&ؚ$ *՘g;AʀVEȫ"Ufd1$O%UMX=fr q2,5ǝdni H-c 4DZmڄEzs0IWj :7EQ1].&)5|T}.qwIل ҂0ĢZ GS0?3L$+iskO"W^=W%Ux&Tb-m($ d«8P2 0dC7WkXK܀0uRoӔ(W\Ɠ?w@TRCEJNhG̊ ΤaAkd:55c`հ(*ؽv-&x@NÐ!u O9SEVQYE;ޗ3OXD7LbE:߲ȠKeopEvph8 F'P\l$vq?=K@MZTnMؽvm%5_哓69Jz UUP^V9+Ew% }yL,MIk3DiPK$In6u::SA# ḷZA4zri`@rDu,*ӸEI@Z."bdN4\VnMس~} g-Y&/+\rq1ag"QCfp1Juw0rzu-'[I$ yN5Bc,c7 4aLig( FA$@1>4BˎIQ"u Iz7y =plu,c|Qfr;e*y™A؃Yb}I&kK](Xf ab,e XkƤfXhT 8qc6jٓ33_W[{ ޙu9o]tqs~F x8H<`$-u =,qI%Xu_D̓c 'k>Dbe2b>DRBY$h/-xw:19+B̴u$` \IRרtXKR`@"zW H<=h3mAȫض651iϲJb[brtOZ鲒U:y)0X$J:+%T p8N y\rI?W>PԎ@Yz7V ׬DH[! pL )%J$V/ |+qsN+<ʁ$ۤP0H-fV@q|͚׬A?ЕF>|KDuOFfxQL:y9X+ ӨH\ AL"ԴF# o} ߟzX7m؀Z6,(e̺45#A}=瘂? w3Q{( BL d) >G%COC"\1W%!J9yEZ"@q IZ7u;zdV8,z̾FMUZnwّ$b %gt#=֙H 0IjyV^2ONY Yh`e9 $t!m\ѬnЋaV0f) 4pH-oM` 4b^¤d2%dSz[ªe/ 3H'Ya)Qg@I}-|AnkVHI30:3R 5irW*MѓT>)zRmC폸_$B>6gDa?iO/REiևlPg X `ʰt֭8"s| :ްo:s`wMI@Z>%1 `w3RXĕ#)+[',F2 ⠎Yӟ\CdS^!Ie.5UaHW1)bQ"3:Jqc+x~&qKE;J"Gh]ErױR3R["Q\.'hX mA~yr#%H+R7(Vс5oW5e9J˰+w\4+:S֬9}&jѽ3]:4(`W@uNB$ e'{n$}3=zHWC|(XYREBOZiVmGBa8CII v#GfzU$Dp%,H+ŀ*e't7 ?,1ɖCQ}Ar# ^hqFLtzr0 RYS9_B30A V0˰Lnf)"`nSL2}Ҕz=Wy J?_܍IVi pn /J%<-q/ 0ڥ$=S0þ4ژی&?Lz'*ZAKL̜ygaUC+L"bU0}^{NR<5A eW&On̳K%}nD7o)9<R$ 19PPBC0.e$j8Xο^{Ͻ둽9H6|wA:a1ħgpb8CVv1h{R/UJWm3xAё;ĒFu4 J5Wn{6ˁ>.TMeJb񘁁=Y'2"%J⸳m#c٥En1ncSSH$ ]҉{2ٍ WșqVmqF {AeU"Ʊ|'ccጣ [~ +4>W,fqpJN—d-BL3T%rhG@ZO;)}fDE/XSX8KH,1t3@^qIkQSc $$柞$byu*</uwڐ&jԇLDo!%lb̲T!JNDWRRJ;$6H2pv/1`?6=sEn7tȺ2=>w:<|6?ك#f<\uٜM _ !SWQ B}4`+h 1czMՁj]~{oTlamuSO@̏}'PÿJw0}xfu)ǂʔXlD+ϑ~ co81h* 'Xumv TQ>ͤw;G0tx[= 3goSgu6ɆF y}M7]Õt(DŽK!IEb8bcpb?#Q'"IErgP҇64Xgů~I]Ĥ!m#&)o;Lyg8u)z3+X-%#z;a{J"U3Q(L }M8#X(a$M2nG ;m4k5xI4k4^^YBDSO`XYgNZ:X3RCn%V0$qȰX(>t+JCt>|qLZ=w482 K$jTu;>D 0D-P#&طzu?f-\T2 Z(7ބߔ9 -cn$) *c%-/Ic0SI,9@o DFQdzF쌪mSG9WSW5XNDҸF$#O#at J#]Bƛx98/vRCݚy#m,È2_slzuəHk 9K$kYpo Hvޠ9NAJa`QBNSŲQיҜs$gv'xY6MO!||(+)>H*|'j54IJ2+p _O^}55gU%+MtAek˄6,-(w%_׈$'Q]s t3386=o2{ ׂ~i0-sx::>u>J^z!0]uS‡Q(jN?oD_DEI3(mތ&&j5L~+ϧDB0 f6'^aG:.aK1R2GRϊGQƠpX}{d%UH&_ѫ?j}SOxkUCi٣B:O'iքSg Ak)H1R˜I-8b dE](Wc!"XHwP@@,[z?#-eteVq9'qQ;\My(_yfێE MҨ =PƘ@"ر tW_%/IpDQEg6}pғ w?c@{8BYdwoȎÇsR9ON?w {W:|XΓ+W}*6b XpvǦlLB(84no]Ǚj=F^~B#Y̓+^ky GGuI*x/8L lK~;AEx磌pK/LE8>3pfGIe2{6aa ~˙Ǘ'Q2y߽[6>@1?Orۑ#x3,S1T+"W6rt믧 y+;lAa2,pnOyQc*>N,n40hZHùI:u#R IDATx?$ Zg&;2 7I}*c҈"-'tORY\h> '5kxSB2b\fI8jNJ+('IJh8  9.w[!`em؀&&LEb @\y%5e^],`ŋO\:$D$U?~[΀扳WꮤW+ڿzu ()!uVS6> q&4`MXh`2i4gOFsZksg&yhɮю+z a{:ɡσE/lcO}*sW,Ê}`GLI^melj|xaЃ 0`lX-[^GV֍7N{ltK0>K.ɼջwg <{9QCtg?Y Y9\-ECz#\wY75#_2۶Yv G藂ag>{t]3eҿy\<(_Z{q&AzXQY8⸳#Rp,<)%|OwXT ]K{:{eO\/^a, ;>\~#ɢ( - I>p0ҋg?K5]ZbYD}58z]^d}@k8LTvl __x!޽[4'e+vܰwo$PٳVߟYDAvص+o]~yHav9{?#GYRvAFQ E#I"'=( LbYF/WJ$cs۩⒊([r1eW]%O_{ǎ9O4[QWl{î@L-HyKqoKM|!aOLˋ<y)Ki<Ϝ'PR[E#X6b|P"VҼjndY`|e z}vjȶ+unDn\lF݂ˮ@Ziv}7qݞ=1.Yw Fj^z)L;D{ '!RAΧktW;(tp&L6h*D#!=P VOH,OZ(ٵ^R[^|1GxĐ)W\TQ/X,dkĪ;EOR$H$`oqo6smCP4o|DJyM~d VczuV,[L6.pEr{֯ǢdU3 i~:ɱ}@ɴ/M&wjRy}YrZzk4nuu-eפ{B5006nD#aFv؄ZkLK?sK3?r𪫰׿JR׮OJI<gF ^mɓ>E?<-2K_-ub8dgqGc^g2 41J96OXA;)zlC:gPb154!cՠb!BG,_1gq3 =*Y2ד.*9X.f @ƀ]{w<ݿTRl>OG%AU&}qǠi/TFl|8A gp a}LT2/?NT2/أg1U r BXهMJ{p *b`]N5pTs  Q]K ը|e ,dRUƂb@qJ(#jWqIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_faceout_before.png000066400000000000000000000205501466047437300256200ustar00rootroot00000000000000PNG  IHDR|bKGD pHYs  tIME 4.rj IDATxy՝ǿUYa (Q4./qK\č'˙Q@pLIL$IMqAшO!Q48;߽֭ukR]}?}w)k &L 6  & &L@L00a1ab„Ą  & &L@L00a1ab„Ą  & &L@L0a1ab„ĄFE«4z4,,X@>05AqPsݍ73qyi]h -y,؞:XVͻpe#؍yDd14½ *@U" Vl 0qP6wA!Ԅ1{W;P @qGh( (P`X^H@r/~ nH*0<^(\0xհ,|Ze )VTl>vl>(|(m*VQ tuw7@q4S.\,$+@UQ!1eMu, (@7k*b L91O=մǥ?bzl9bT*(B`xԂCHD5-+$ZA]w>6p *"ʥRy"TV R =O1o[g1\TEt [P@ފe)ٖRpxjbcYT*ÅBTV%]^%(ccqjү<nJC*t6JR\@P-H:U cj?{~bm>9*x铧$P~ҧR8|G⸶{/R3f`H۷Q(-ڜjǔFs%CIy<&m x#V*EX(U ʏ@WP[0ͲX5 fU2%> QhP T3롇n- gr\0`CVPPܸhQ{I[YpJ(J{7 1kV\U@x ImL @T;?/,d…o OETŐRŒ,'BPxePpb)]{huH{1RnJ3l3 o2Em< 2^ʐMO . :o"@j }@{h y鍊0M*p pnT֒YԵie-"-Hk;$iUA<nD>G{ -IKqڴފ%' 鿠40Kp0IJP␴сޡ"v--VEI4&F0Ս7b`GGw@ ! T sJ|LjiI@^{ Csu͸%M$-b0d*n9)G`L94^Z-f} &x1@\`A,ٺ bvO8lzpL G.:y $%*b+}cdӴipg1|F=L4O =dSeESMt I7^*29uK*-({RHV1}nob3zBQ .S) JS%[0p]~a8wW[ʍMl1#Xԙ2='[]zvԶ* "Ubi[-3؜:X:BGF=Ul )MvV) $)|\@$uuaPW?CPZQiJ[Ko~cZQYfp+`A }xZQl(vvv<"_W$ 겁H>seOEeb_!ӧcr-1ICXW FA$e F wgBHyT}?Au%)fBU ]?\. -(AM@r ˨G]bu H;8;;;AK1AP. P1J%mIQj. 9;,BCT,{1qս>ثoC ʠ.> PU) V@Sz$U,/ P\{-F﷟c?z$L@k yj`n"X 㾯|c`qs73WdQ Af7*ʼ9 ,6$iȞDR[2[!?׭ ҧ,^XIU[7 G<i^z)u!Hf✼s-U&Oƶ*W,0ܜZl 9Mrl,TsY 0[Ks,jUf$kde5Rjg 0ۨ3kt,eՆ$J->h!;J$_<|&.*MيqgeHwbRABg5 ,n ̮-Pb/Kk4 ]O?T'RgCHt_gEF@א^BgP93X\$iYԊŁS", L 8IϫԛF"\ɐ)YaiNZht)+Ԡ"O }9=gȪUi3bR9ʻ[="lߗ!SQ*Ҵs [nnp}뭑2>$#ig*kQiU>+h޼SQpB!f tpJ6Vt-sncUG|s۾]D$´72Gƫ&&=+M}h9f1FhF5NMП:#S&5-o9x-ͳHS[o˞^w]쪫QZ[Mjc/ȸ'$=I>IS*bqT8FUf-sf6͞Lq@)`Q]R"G gkA!GR&^T%vvv6>wX#|{HJh2, 3g662W4·Bįh\ٟEwaq_ H%!c#+s9˗<8 5zo, T0\FXDG}₞5 1rK]kSd ׉铸2c}RjPy$#p*'3G2/?dPo}' :p ɷ cd {矟Z"=5 mTO@ J:pCLD_8NYJ׊IHZ G*~?#\g_LNTc$~CS5l P1n ? ʽYg&RbN_(Ὠ "ZLҜbd]LBeQI?t<,*|~CQ<~…/4^Q,jP@Q,߲#CAibg:2NU@Mb!LAt*SI?1chZ\>qbj+NLy2eJVi  "+1b єTH ''e\PY(?Y UXWOLSO%owǿʐTĻσA>:,IŊ&siĨ`^E>^i DU)5Ĩ]8ub.He1(*YeiRVFeeKG|;le.B5HϑPhTKCU TQpx^)fG& g(0<O=6lX=.رCKQH,Rs$AwukEф#ȻKseiVIl=DTҗBiTԔ[G>{_cGo?\׿ƪ^Q!#" so'ԒS7oL)p_G \ueȓTDT$ͅJQ=2R]T(./9gm&`Y1mxu28k6sjHăa w-[i~,+V*VJkr:D?ޤ#ӪN?pnҋ%$+{l|k*z$I#҃d ?`,/&$I^Ղ=aQ-<5xq֭Rھt'i[Fs0L A^υ˩Q׳lD(w@S. ‹cdžC+vnBBW_ y'G䲢lԨۜqSP^_[zz=:~re榼х)H# IVQJժC Mi{Xt⩑#q;D%*Ȋѣ3c1` 8:zj5똿zu3o=n(7@RY}Q}e$eHʾei|fV +Z/jwl=X|})GO.a9‘ ٗ wvƮnɢ8~ɖMZ3v,{mҨ?7fLNٴ) Czf(|z˖s5Vo*NX.ouTwᨻR1pf+m43i,U)ZudιB+87ÝK()Qg:wR@Dw5'C.:<4zuݻ1?m(YA]]=7t,+Hn9\v䑘n]'| AaNrM̋XʕKU9w=ǶߪU>,5ӪiUUˌ|`3bt"̧6l3ڸQ0ĤGRt뎃$]ʇ8Z k5Vme F6#q&-$^_>[JEX}Uj%֯OՄR>2@ꎃw;CWK5sjScw]*2U#N`=XwJRK7yf@-B5S,(Tj[1",K8x {*>VU3PΊQUkÿv(`Ďƚ[ѲR+߽oqȑ+0;/ǾfH-ָ/&[G5:<4 V@խVyUvUp;7B$qZIWTG|&pORA}%j+G׭ʃ#=W5>8 x@)Q==,e,L.?EL88v\@xE鏷q0}ՐB"Y^ wi?+Q~9_9<(1pH5 RAp_Jlx ̐+as 1D9Ѝ7: GZFž+W|tI(=,op~cW==8;gf26k"@׽bie`jصkLf)CN@߃S.[:VG==yMYI{Җ}vCFvYAOOlw*:rBǦc/LWT(ʼ^r-Pwr gF372sHR1kp2iP9]'fʐ؂8#׬ 5Ԅf7TgHg'j#j^5㻪U|{77/K0eIW}V7NZw,_>jȀIO ^Yt<yyӰk^(\3;o^9rU􅺤A^2.'NDK_b !nC\DSq+qd_]⣞%Y$OX;kev`}(6N=˗S9J9!합Fhܺ`9QQNU)4{Ԁdc1u*J(Q (Q@*)#_g#l="5n ;1{>N&Z*kŐJB9'mb unB% 74 K? _󆙵3nY@tL MwM`Y!U!%笳,YZΥQ5'Msqޟafz<Րi:8|[ [N?ǪO^KʹiO]#ci6)n8/QC!Er48PE!ձⵠ\2 "0xKJ )mvJ @{r;,> р:n0*4ܫZm' fH3X,ELz-#"@@|<лlqv рcxN t4+" &n&JI/u @}n/@ͮMUzN87uH0L9NniB4n:X+ 4+$ @@. )V8b XHEz#4)}Dc3_!21*`ؚ챏{ 4\{}t97=6^g'ӕZLηTn/Uq|GƵ)$bɤM\" fQ }xehn'/tHvB:) vHp3& DTd:YR])I& Ah2v@WNtXj<P9VO^/⨞ "־X<, i t @D9xJ HAq44ݎB9FzRuA5 DL4W d+*hP+@)VHEknxns;hv\H4Ⱥy1Gr/PKT3 BB0ʒy)8%|n}y99%ϥ˼U}jO\ږ {=2:25IP~^ҫ5 REZ֭ԆmZ'% w}AnzAKT Yvjʚ4{8f]buYs@GSB܌yT^mt @{,&QzYZ5ɹV6@K߯b!˗:-(5e.A˥c/fZ/8.~!6v#[kH}u]xn4A]BuuXlFjcVx>@k6Z+ZЬE4 DIZPZe`.& Npse'7h}R(DL1 tWC!Ϥ7z9~XrHq3QRx f1 @ƻ!2rv܊+ۀ8{wۮGٸB4nGK1}fǕ>:^ kdK!ͯoqmX:_b{p76նW| H~sAkemGѶG3=ΤXDnMdsnW<-"jU֖;D9~倡4J!7DUzO 0xH; 4Sʄә@\=x)2 @XykapPpGĢn,Cjエr;7Oe=B,ӱsFsͻRek[.EˇW{9",[-!+sښ"B$޹jѺhmbcy>d4=th|/BX^#@c# 3.!ˠf\{%+yx p3];̃2]3iF{]W:k@Gc帆ZpM:W=?;2 k~āߟO55-5@m H)Z||kJz28䵹KwV=ŤcS:qCt NqPl3>7\KӳG'mC:־@@wNGz|hp9@Ǥ9Ԡ hp9 ΁T8y#h aX^ hPy!8vDJ r7ɸ=i7aKn}ڭ ,An:l¾z!ms: `{iOP:VhH9x@ň&J \ b+\aw:Rq9ыZWJs: pYМM.'Ez `o \NhZ鹙MQà0R2VXGyjKf؞dJ[O,p5ĵYٜ,89Vƾo VZκKP\a:_ʃ;t2Rarh.'tzP.KРQ /-hz.|e]a,h"(CTBpMDEA^6hJgF-D{+W_CWʼiXB^zNp&d@ҰtN4(*< .QhPy%Rp  ^׻aU*,=x ])i<:M(ꖞ:ġs5:ӕL EFhPFZ a!2`/ t/ ]WkMs2ŢBW}"yX/sZL${K(K/sǵE%BHU@!tB@!B AA!  aMiFIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/example_simplify_before.png000066400000000000000000000121471466047437300260310ustar00rootroot00000000000000PNG  IHDRHQbKGD pHYs  tIMEg IDATxn8 Leͺ~(<3ͤ۟{|W j;[UShmޫieWT'V6x{Wicb{RfIFW^D37MkFfB6y |42ڔSƫ=,ޫfYxFx+WGk_ۋEFյWz ٟwᑞc3ǃY-O'3K=e;):~fxv`At xdsi@ԍ.D:Rp9ِ!V|^"sX/l?4}i/ܳ8<*/)"1@67 *كK{s^=:Ag(`z'_ope8`**g+پ4WoTx(Nw."Cp6(lx"<=rpIG7AvwQxDyյ UE78ѩuӣ36'F}0ރltgu6C,w+SDlfMፍsBΙcXԆ2V`-#)e߳yt &([<#lL%fyKLqЃl &Xq1 -NCNCx 9jDkB=B!,< n@="ҺWoݜ)3Fx4 hӠdAmfPsv6/|Ղ} "lGY BCkg\ lP|[WR7{ƖhlЖU%zuu5l|g;&+8 }%~%DČg3:/7=/e>"hU^Q9!{8t &۲,g\x[Gnuؾx4cqY9$:gutOIq>zm;6^&Z۷殺yԸ6e&i=Ԛ~jC vQ2yer+:ayxUۉv勖Sg[~l}Ɉ͹̳ԑ #:gt$BNhc;\_M*Sp[,rg*tDb=d{ʺvKg/=*dS'8#Y&FN5u"{63ëԦ5C(AuSg3T3chA鹶Y~,(D?49kwDd'crhʌvуk$#lwDUdHUxZg.$=TD##8ЬINA3k'ZuTl沙5DsNŁzϸlH=MݫVB9T{݈`eᬹ?/Y,͊'\ć.<3H=2j=Rrrj8#Ԭ BtZث#}i@ .m O![8 d:ۗ kDd^M64jpIddMԙΨK(x\[B6l*^BS^~(5c1\A(Z,xv3z4†/:' ( ^Z7AثM_&^Og!m~Ƃ*ؠ38u;lǣ(tZR@|D61FO+RpFk%gWf̯߶nQ]ϵu%۷:H-skw֫vo~ov-u$WG}窖yOHּZW=8|{f֐>ګd%3cyzʲ>ͬ=qPæo[c3]kW#nRͶN7ٌ]]+5"H35٩hDԉPp 6^d#~J0E3W*d3/*sq!KW'[J"),^E%$%{6fFR >OtkT@apMX8{K8p?ﲙhuƇXv{d/B[ZU[a? 6'zgrNޒ}n["96ZtFhezgtv <(b BQlj\#A99+|:H9Nd\W utp!tHsWO$6hZÊhpY#3le9#fVz%eFXkZ}8*0 fCpTAa9ym_ 40h:khwТs$<Ǖi.ĠBR.8|Ez8^RxwC[BtD s\d_p؅\֌Q1l"< gdM6|"AJT8GC*W6ۥd3g.Hc6'U^L\dC6aEnJ́FRƨmY3.-+[9\C3Hݏlh-~Z)+\od-km5wxMQ=`Ps[RZek mbjx5JDԢH/ = Grz I>H庥s9DGⴿa*jyFaP .XnE&|\zޟPtZ_ڃh~D#RRxwśf:p,@#^=/UrߵTo^`2d? jp͵i4TSPgHx9XOA\dC6GDZ.!Ytvp Dbݳh;c#ңxNMUI>f97F).UX'B]Y^ !C4ѹ {hu%D+87MQGWFbKIj~eO*zPId#etCR7'P#7w^<[qz m>l~A\LDg9YN͕x&\Ÿ&O6Uf;Pԯ0E6lsyѸ ϋ%a9-"e>mZ-sF;^ͬe>"ߏ,ٶ ,9hFѡhFѡh4Fh4EFQth4EF(:4-$<R(IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/viewport_ortho_filled.png000066400000000000000000000047501466047437300255520ustar00rootroot00000000000000PNG  IHDR,r|bKGD pHYs  tIME uIDATx=oFSΖBA-S_ѫ7 T[&fX Co,]dS@ZD>(Q_@ ,,@ @, @ ,,@ @, @ , ru9Qb2l6jLŅ|pH%B!,OߖZ-u1 JUUa:>/`!BK S%Z%+Jm GH1NEM<֯i !wOOBUUxziڎ`Ati4>_`i}1D+%XG``!ZFZVat&%Xa&D˶뺓^-`%ˉ+Zv- %X``RWeEK90BPuө MFZDKZh!X9heUԺ=X%XeXDh  h-{ ~}Tj`ARDKzC ƴA 6J"h `w%XxSl0Gy,BL5h FZ Imh hii &I*5֢%Xkq ]^?i*;^7͟A XGr?׾iYJ`E>#Z`Jxh5[}ݦx,G9%tP־#^4*1u]Um$z9//o-h]'EJ5祫n-_ۭnNGdu4y]WSV){6D|rXjEF"L/ӧO4\gn_h:rzg` - !d i =o !k1_SyAM=V Y?qc`eLT#, 𐫇7w!C*c\=9D$ᩛs'2=VhY"(,>M#⚖`?0MTB`*VMCrH?5,Ps}6V, -ȣg|>hYr< ַ4]osp5^i QX}ė/_h;El MC:ͣ'''{LRiZQVG-~5s\\DX%X[Xضumag-h Vzi=htl#VFk`u!%Xku=hXL1ZM6ZmF&S"urV`5 PlzhS9ڴGTp+X%EHoCX,D(aIwP2np77^G .߿o+IF+Xe h!ZhQl hE--*.ZCrЋ(whBqPK[b ha%Xh!Xh!X>C{Kh.h`6Oz∖%Z:ќ`Vr]s`Vy %Z%Zb\KD+q-Nwd`E-{Er* `EKK0,D ,,%\ !{wzz׿pЌ'=Vqъex""XE:ZrH -&Z%Z4FZECDK¢e (XE2w{`- =L'Z͹5'qnǦo }h !,DK,z SLLJD*ߟ?G+Bh>"$@D(bPWA\5, @ ,,@ @, @ ,,@ @, @r8{IENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/viewport_ortho_wireframe.png000066400000000000000000000101761466047437300262730ustar00rootroot00000000000000PNG  IHDR,r|bKGD pHYs  tIME:6 IDATxKrݶ@)'*e(/w(mƫތtn4T'4yAߧ/ , a ,@X a@X @X ,a , a?<=:(?xq @5 vCZ JBZi𬀰`A iuBX0.H ZAZ  dD@ZnaMQݨƀa ^+[5bB`H VZs5LTzV7EZ "\#-FkH !-@XKH }+BZlwx+eOj!F$Z[WzU~U*SVg"CZJ<BX0BX0BXDG`H amRq}V i!!f5i*0V56Ry!-ݫE`H amQYw֊CZHkU`lOeبb`{=_(ucwU> =q;K=o I,˲b^_#GEzCOǏ!,nORgRHBPS85R׺Ҋ V.;R DZ i!DتK$|*&B!0VT,m%Z !0Me]}vws iu2ȹ.$iR'BX݄eYC^__U>ek)DZCZCb'AҋiWu2LiHM_)$!Vm`($F틪R#b $v~_aAP9}Cn#~)gTBhQZ.9e;w*ڧb~i*{Z#DS"g8k"$~q/jI_QO+%^N:}a||(#Vi _l\9OYf=M K%ԲO9&vnLpsrt(=\AZhQ"8߃CV GX+MN 50FVTlԎx BZٕI IzBaM҇իJfK;KU[B3R kQBѠH(u?߿ex?N!OB`RcrIVuDV0(aaҾ뤑bigu4=Zɑ)"rGFQ k9zif|/-L%tkBiR42+-ol-hr]fd`}`CK.P]3ߣ98R"}VEXEɗS!?o+Y'i-J7jDRʊk%[^^iIӱSeʕjSa*YshN.y_a1yjl7 ͅqI#KQ>-9erZR_a-*rFֳ#^,iϱz^:;n␃l\~dg cuJ; ž 8ZJ"/}l](RVS[^J$I "-"G>^iW(=d`UV)0+YJYɉ4Xݳ`j2+BXJ8r#thL3-ظί->DX#]kP-'?x=̌Ke^4}%c iu.Y. }i6NoR؜eQ9e,i޳dBZFf,Yu+-|4kcew$ceUl{j_i_8k4o[b~;߿6҆ +V:UҰqj(YȉZQh"T>a9cD4"YFٚY$eY~F掊|;m$R ɪr==vZT>_JzO9,oߎüj >R%G!ͤK"=uS[[j;V{[F_5z;,~vii!!U㐃==D?vjBXjYlq23ne"s'")Yj҇-K;kJּ_hQks;7sJ]="* $4.V]w`K"B7dUx3]ZDX裕 Z}FcOo+ȣa(a-&+pJ"%ir癕ϺLKׇrBZ\X%vݜ봍˓dm=}k%iчUاz\'s̾i ڴr +I at@*ZcJKC{E=}wrȨd0~3#}Kg&mчHVbK9z}y9jҲiDXDZSGLVPZ>:r9VE0r_bU/fI?{0U&)^(qZ* EfFDZ9r)Y˴w7)!(=Yk5z}3ڷ`y 9DX#H׌TRPYtِvê֠Y52Ci<LsշAXHFVT)&-]Nd\kϩ%LzY&"IU~Oh!BN]H.CiKdC>kчՠ˪ w[R9S#gugޥ&Z|(jkiw>vZTVtV0J1^җ]603IivNzZi4-9EWt}/Z}peWhBXN[y_nHq^ey#5f2)b)(}",ݻ=p4z3őX"MS-"|8j箋GruNlI$$id=x yu-!ɄL I"ғgz jPKsb26UIZ"I(6u,^ޯUZ~XK$!6ĜFuAdLҺFFA5ӽ_?_5~3pIVHTѥ+>9v?ɶ=f>W)85EZ9K%dwSj+CXEv hMnZkh8D>)Sh%;=XH  i dpF}aM%dJ>繇TeRB>vT곲NUgIH ȧT)F$[.tL.am'HJdW4Uy"-WB HEӣBZ[C ,!F(75_(JsɊքYVC\ im,du-^IZR#+RD#BX@W6gLwBZ6yC5ܣxb +`JBZ \9XkJ{NH a!##¢#+‚*}ƃ‚AVBXH4p'}'K ,K] +`J‚)*=BZ  -*ᅰY!-CWzdLQUGʍ"Zܕ>$ rBX ' aNB‚AVHh=a6=?4C` X` X X`` X` X X`{۷_~_ M/_LY' *Δ%X`zB(S ʔ%X*=>>,jLY@8!Xᙲ BLY@`!P,K@(S)K(,qb\MY%T@K(VLY`)KeӽWv:͇ e4z6M q ZԆisY\.Po>|0e ~ؿ4ϓ)+V,0u}Yz͎SSh,BB~rɎW&S`SSl:O/֖s5^X+,;Xέu:J]^`6+V^ӆΔ%Xtj*) Xtj*%S/nvP,w[[ *(5!Oi99Ѳe7h7|n,Jݯ, NX&LI%#w ;y,XXέmJJyv֧ϵ/2aڃ,*LM߬KOZݯu/VRUq& =枴jVp)+uOYއb44mQ Vlb%XUDkBn?+6J f :1m=ZS8RSr^k֩)ֿBL o](rh+{jJyܺܫp)қp7e jԳ1Su`4wbZXsT+X,*:5Դr½[`'*N %,Iw?ؔPJ/9ZKE)k֒ʙBqsڢDL^ց4u@ՌϜZb;!џ+wzq/{+{JOX%NDK9& ߧ{=%s:Jj/ KK)eb}6kL[% A5{[SU"Z4߄+vښ#ZNF$L؄PM]X֭݊+)]hCS(یع%Za?F!=$ViW&DB&~Bw+XMX%++gNZC1BDkjzRhދ.ƪGǞkpR2S`*-ƽ:ۍO2Ãhsش5וD/gJ IL[ۿ0Е眠 1_MR+~baMXӖt=!XA O8KC- Aj镇 T3%9ҒEˑsВ0zh,S{}Q9%X{ SD\v|pEߤ%XӖiӮ5t:N&{X=^z坥`!Zŗ9)VWzi#tĒ-En;!YEkafQ7*~lgݩ^H\1q, d)Kx Vo=ba}ma M~x5U+J, }L}>{XG۸,4Ēr, ^;^`AIk*F9ʲPҴ5QbWxYέXg9/XI_QršQN[BcST`RN PfԞ =#VKZ1aŦ&W  yKm9)XPqsLj5s1aE#w3WC>=?x*`m/+3G\RD{X+, @6lׯ_ׯ=(Xҏ?DK-:+-߾mh5z#qǏͻ=; &]eDĄe¤` %Xh8{Xj^iL]l*Z-.DD B ղAhٌ7aa2i!X…h %Z==ek]Ჯee` %Xh`!\E( W2ѲoԵh DD >{X;W>Wh2aa2i!X…h %Z5{XWi~}-`rPm 6L` X` X X`` X` X X`` X`y,@ @, @ ,,@ @7-_2ZIENDB`mm3d-1.3.15/doc/html/olh_images/screencaps/viewport_persp_wireframe.png000066400000000000000000000104761466047437300262740ustar00rootroot00000000000000PNG  IHDR,r|bKGD pHYs  tIME'k:IDATxM~6GȧeKֲ}º=4=B63I(YA`)a , a ,@X a@X @X얿9p^/qsB %{@X*w,) al"'<$~'e!,9E)v]ޑf EBXHPDBXBX@yDL/w,@X@zr8' ڹ) a a"e¢8KqBVHY@b OK %Ek>u:Ax07al %O[=v_j7e" bNK-54^>GJƱsR5|qj3J+hV6z-֋HX/v`~5IϤի6bD)H aM$$efaxX/(iR*5{J"}]o7y4'WΆS&/i˯ᨥ_F:X[Zz![&QYgŬ aHX=njojqFFJoH4q5!SFb #c.a2N88{Uf%I$!C2)հ HX.(-*-߬K(->RRv,sê/ Te9. TZʋ-,=C>%!yci+:_>&eͭ6iiod+FRQ##Hkdڒ.bIK->_LZo:!,Vͺz ek3'I}EX8{ίCX'N[ҥKZd2jQ@Kc{ @,8-iJZ ]I5EXe E|Pe*Èǔ6ɏMzďr+c0+'VN\M+-謏,,z2!~#~R( 㽛}ZJxryߖ--庤t- `iK\r&x`龸eWX䶢&osL$gBд9Y;@G%:Kbi)Fk!?yWiz|ۚz%:ynuv3/M|= f4!VS|'aGjp\C?)?/uj\:ڝ%ҒHXMiKFZzT)?K,wjqMt aX\44]QϹd[KK2#|ϵ>AXSڗGJ vIW!-iy:R2mw9IOJmÒ?}y,=KeeҠJ jeYX*kikTteYϗ==o-e0!z%rPR|skz/?ߖ_i}\iNUn%rj .ir&}_څeHr*ky-y )Qj-^j_JÖt_n=7[KD[]sJK3ߤn&c֯ʮj+"[@*J㤛zZRH a3mPZ8L/ڕ"Fa5KdM)MS{O$,{HAcuke ů߷\CIcsA7RAO a!S Z i!—|[XGZЄ=Y,BXW4 qeZ_CgdqΙtN~|~ q8 y˪yDDXbZS:H aZ\vYmuސB^K--aa9s3(F a.q=uid5y`‚fg.= ,# {576[5Yuo}-(q64{$i!,k^Q9BX Wϔ:s=U6`bja:@נ& D= ,hT]詊aZġtl\ % -ӊk i!,\\> WI^q2 I$A\ʊA#7?h@n^曶EJ>",$YS^=BXpB!^^IPd+\%a , a ,@X a@X @X ,a ,@X@X @X ,a ,:[Ԁ=IENDB`mm3d-1.3.15/doc/html/olh_images/tools/000077500000000000000000000000001466047437300174375ustar00rootroot00000000000000mm3d-1.3.15/doc/html/olh_images/tools/Makefile.am000066400000000000000000000020401466047437300214670ustar00rootroot00000000000000EXTRA_DIST = \ atrfartool.jpg \ atrfartool.png \ atrneartool.jpg \ atrneartool.png \ bgmovetool.jpg \ bgmovetool.png \ bgscaletool.jpg \ bgscaletool.png \ cubetool.jpg \ cubetool.png \ cylindertool.jpg \ cylindertool.png \ dragvertextool.jpg \ dragvertextool.png \ ellipsetool.jpg \ ellipsetool.png \ extrudetool.jpg \ extrudetool.png \ image.png \ jointtool.jpg \ jointtool.png \ movetool.jpg \ movetool.png \ polytool.jpg \ polytool.png \ pointtool.jpg \ pointtool.png \ projtool.jpg \ projtool.png \ rectangletool.jpg \ rectangletool.png \ rotatetool.jpg \ rotatetool.png \ scaletool.jpg \ scaletool.png \ selectbonetool.jpg \ selectbonetool.png \ selectconnectedtool.jpg \ selectconnectedtool.png \ selectfacetool.jpg \ selectfacetool.png \ selectgrouptool.jpg \ selectgrouptool.png \ selectpointtool.jpg \ selectpointtool.png \ selectprojtool.jpg \ selectprojtool.png \ selectvertextool.jpg \ selectvertextool.png \ sheartool.jpg \ sheartool.png \ torustool.jpg \ torustool.png \ vertextool.png mm3d-1.3.15/doc/html/olh_images/tools/atrfartool.jpg000066400000000000000000000010261466047437300223150ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"&!12AQa1!A2a ?fIXq !#HD](~r*"0S]3 .G vFʙ_a44X`54Mɨk"gj2͸PeJ5ӥs:v!t(p-WlL˺ґpH\D&毽hnt H"\nomǺRHjܒRmm3d-1.3.15/doc/html/olh_images/tools/atrfartool.png000066400000000000000000000040441466047437300223240ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME 8}qIDATxYHH IENDB`mm3d-1.3.15/doc/html/olh_images/tools/atrneartool.jpg000066400000000000000000000010501466047437300224670ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"'A1!"Q#2B!1a"A ?DlڍOL=PU9Q>>k,mcXwO>\o$m$ 31y$ԟi|A:K׍Pؤm[vŽM%݋r^YW \AH"I+)JA4.d&uv[Y8[)9;$]X)9kmr4q]#"cUU RuWU[;vRmm3d-1.3.15/doc/html/olh_images/tools/atrneartool.png000066400000000000000000000040441466047437300225010ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME ' RIDATxYJ6\>IENDB`mm3d-1.3.15/doc/html/olh_images/tools/bgmovetool.jpg000066400000000000000000000012131466047437300223130ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222")!1"AQaq"1A!"a ?wEꚦUMA4٠JIsd@xu:% {Ң:#H$Un1eE8:F݊'cȈ 5s1'[j}3UHEg(\3 Ǒ?Wv0=@Lu<Ϗʺ%mEJ)I n>tԆf OD'j,ȡ_{@=P̃K*ihFI܃,WX٠47K\PABUX}馚,؊ mm3d-1.3.15/doc/html/olh_images/tools/bgmovetool.png000066400000000000000000000005741466047437300223300ustar00rootroot00000000000000PNG  IHDR$xbKGD X pHYsHHFk>IDATHՕ1 0_D(m tqZcs2zt!xnM-jĨߒ/?^QU#dF\.ahadFѭaX1vnun|)cր(D&'cy~8<JIKp DkoApo6ϲ8-cuS nZeXKB!crs9oJ?IENDB`mm3d-1.3.15/doc/html/olh_images/tools/bgscaletool.jpg000066400000000000000000000011501466047437300224340ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222",!1"3AaQS"Aq!1Qa ?8K˩sח7D8`\TlVp am{w q/2ݸ( ۙw;qu`F /yJw4ȫͰ@<´}f?T3Ԅ Ď.|A1~FQI۬LQ3 b| Oi]^́]{*xjQzhAEryoz^Ne# +ʑ;wڡ7c?ʔ_˓#gnyLYZA<݂[/ʡ+(H{_RL 3kTW03mm3d-1.3.15/doc/html/olh_images/tools/bgscaletool.png000066400000000000000000000004751466047437300224510ustar00rootroot00000000000000PNG  IHDR$xbKGD X pHYsHHFk>IDATH헽 0d67Wǀ/rO!\ENWKK116o0 1$gRPJ) "":Zsn W.xfihw晨# !""aӗ¶(armp,ey:)Ʊ,-Wři@!l{u  ;>iADD\FBZ=krBK2PIENDB`mm3d-1.3.15/doc/html/olh_images/tools/cubetool.jpg000066400000000000000000000011731466047437300217570ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"+!1"Aa#2BRT!Q1 ?cpD.öӰ et*;OׄV{/B-v I+@P{]OڡDѨlҎ4LxQw2&3ګIRF/b ɒ0:VAr͐1)6$,@ֆə[,ⵊA`p Q ysMTϾlC":AF 4T2q>T{w կbJ\J)qPhFOMf`b%[ lV*@!Fd,gld$ I=I%PU.{__mm3d-1.3.15/doc/html/olh_images/tools/cubetool.png000066400000000000000000000040441466047437300217630ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME &RIDATxY~~ ttEECCttssrrttrrss%Q+&IENDB`mm3d-1.3.15/doc/html/olh_images/tools/cylindertool.jpg000066400000000000000000000010701466047437300226460ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"01!Q"2BSUVacq !1AQq ?bH\xp_hJKJm8NjMjl{(NFΙ(ŷi uM>AÞ%ì:!M<t<7zJҴhn.<72m_caqqE[(壬wXG[O*9x\F$a|x}}_:?`:?TkJo݁2CVD X0r)i *R9I9kmm3d-1.3.15/doc/html/olh_images/tools/cylindertool.png000066400000000000000000000040441466047437300226560ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME :LIDATxYIENDB`mm3d-1.3.15/doc/html/olh_images/tools/dragvertextool.jpg000066400000000000000000000011651466047437300232150ustar00rootroot00000000000000JFIFHHExifMM*C  !"$"$C"*1!$2Aa"BQr!!"12Q ?ɴW3lk,nwFEtC3GQ-N&mRG"fh9üf1.8< m5dڿTᦐry  / G^FO`qpW^ûQtͺF[ډecE$]5 n0fS k-j#}ElvsI$g$y d<7m1;z4EA#4,7=dvI%I$xPrW$nVmm3d-1.3.15/doc/html/olh_images/tools/ellipsetool.png000066400000000000000000000040441466047437300225020ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME sIDATxY-hSIENDB`mm3d-1.3.15/doc/html/olh_images/tools/extrudetool.jpg000066400000000000000000000013021466047437300225130ustar00rootroot00000000000000JFIFHHExifMM*C  !"$"$C"4!"12$367AQRTqsuv&!1"AQa ?9 6y-Xk`=wbr2#κHsIflv ƞ,^,@|<^_HG׹~w}҂_?_7/BְUmml` N Ϯ`ֵ,ZøW8VkJ傒z # V9ui{kzi翽Si!'&NRuQ7m5 JkO , ~u*_0O@N1~y3gM4)ɡZoNRz m+nV.\QիZz(J4{9€@Pi!(O:];tu*1 Cmm3d-1.3.15/doc/html/olh_images/tools/extrudetool.png000066400000000000000000000005021466047437300225200ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  tIME ngSIDAT8O @[a0&}(()wɽlZ0ec)Eq, "GYDRCgR8@DNeg<\*PaMΥsfs@0j:P/3kW>ߋ<gc(锷uYėc(iw?å#be2~υ<#!0 RǤ#{|0IENDB`mm3d-1.3.15/doc/html/olh_images/tools/image.png000066400000000000000000000004321466047437300212260ustar00rootroot00000000000000PNG  IHDRKlbKGDC pHYs  ~tIME%{]IDATxՓ DUag"==*@Hd|OdONc@oIENDB`mm3d-1.3.15/doc/html/olh_images/tools/jointtool.jpg000066400000000000000000000007761466047437300221740ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"$1!2"BA!!a1q ?g[][)Si\g13G4c Ɍa)JG`䓓F Z5'!uSZ:'i3 >MksZ!.˝,%(m&ӥ7>8߅-z~b[U/YBQ?c3SkSlzhJQlKčۤ;2>=C*L׭tH1Hcmm3d-1.3.15/doc/html/olh_images/tools/jointtool.png000066400000000000000000000040441466047437300221700ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME 7!'rIDATxYi&C7nIENDB`mm3d-1.3.15/doc/html/olh_images/tools/movetool.jpg000066400000000000000000000010321466047437300220010ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"%!1"#BaQ ?H[`<3a93pr([p6Ic}vHܣ eaH:$h"[zY㞼ܷ#$RܘR>v6u@Qoi(Q2OӍ[rVC24m9a6WBN6 ČcIUNSE[Q=)WT,řY1$ĒI$N1Humm3d-1.3.15/doc/html/olh_images/tools/polytool.png000066400000000000000000000003521466047437300220260ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYs  tIME RwIDAT8; P'Hg#HO$׉K`)C:5ZW5>0YMWuy8[֐*I LFb9AIDATxY"XIENDB`mm3d-1.3.15/doc/html/olh_images/tools/rotatetool.jpg000066400000000000000000000010221466047437300223300ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"%!1AQ ?fs=7[ Ic$>Ok3b@p뗔LeEUh{*MhG`7ì댚)HʆcZVo?@2)A})Amm3d-1.3.15/doc/html/olh_images/tools/rotatetool.png000066400000000000000000000040441466047437300223430ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME \aIDATxY cIENDB`mm3d-1.3.15/doc/html/olh_images/tools/scaletool.jpg000066400000000000000000000010231466047437300221220ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"/1!"6ARUaqu ?jO'ҿO\m. */&[ò@7-Ͷ!tjkךߎ۬^(Pq&<9D%D8J!mνcwxuFsfyW_+0^APJ3`'ɧ&Yu@-.ER^S>U(*~dyՁ`BV n9 '”mm3d-1.3.15/doc/html/olh_images/tools/scaletool.png000066400000000000000000000040441466047437300221340ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME nIDATxYQQQQQQ   CXIENDB`mm3d-1.3.15/doc/html/olh_images/tools/selectbonetool.jpg000066400000000000000000000011441466047437300231620ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"#!1"Q2 !1A ?8O*-O:ͤz!+@A͖@ZkL|B!`To]Sll1(#ɼZKr(b>5$ YfWo ? +O92rVOII5GMT5~sJ.ku T]S~Yו!hb/Lc蜿˝e=d_^kj XsJHS YvD HiPb#`E^qXa .6M׮I4롺Zht \ nBU.|Y;#|4EV ̜)m?y PJo~$ "Pxp`kn =sd-a~#xm֕?޶ֲ,aWV m GziflqpcW7J+-SVtx `DNM4ԶHKp(5R̘mm3d-1.3.15/doc/html/olh_images/tools/selectgrouptool.png000066400000000000000000000040441466047437300234010ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME '<IDATxYa9eIENDB`mm3d-1.3.15/doc/html/olh_images/tools/selectpointtool.jpg000066400000000000000000000011131466047437300233640ustar00rootroot00000000000000JFIFHHExifMM*C  !"$"$C"#!"1A%!"1Aaq ?89B ~6|Fd^ӳq˖6?|82G%qIY"fJH$)"Qm1ydmщ4./Hqsf/ #Q~Ca9aVsDT)aRm67#*&4q^(؈b3iU~$z~9ju*(8oO<)wJhh4UXҲ>:ބ96 F /wR|oƶԸIENDB`mm3d-1.3.15/doc/html/olh_images/tools/selectprojtool.jpg000066400000000000000000000014471466047437300232170ustar00rootroot00000000000000JFIFHHExifMM*C  !"$"$C"+"!1#B23A&!1AQaq ?:i;c߶'hmPZ ZN\?mlJuF.ע4Ltm(EZąy LEQTєNd֏r~z̷\ٺ.x~8S@O50ԗ]QeRxJHFd*-U}WΪdČ-}ٱ:%զy+JIՀBά lBo6 mЬG)0:x8B`EԲ?;rOoM$0Ć&|?.FWr/أI M)I7Dž Y @4/(]b{ϣQNql$fHhhEonizFe1NjlO}1$lc`#s/MmZʽA xBF$JHjao)Jəip?GϚRmm3d-1.3.15/doc/html/olh_images/tools/selectvertextool.png000066400000000000000000000040441466047437300235620ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME 0CIDATxYMZ>IENDB`mm3d-1.3.15/doc/html/olh_images/tools/sheartool.jpg000066400000000000000000000011011466047437300221320ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222"*1!"24AQau ?{)lMGڔ nh9'SzINay2:`"A-xN4ړ,;>y+w]?ʡ r>Z(i>i X{$O UoW\n'MZeNh#he\;}s]6Vo'#s类ݏxh)˘Ò^})vkR{) pTFp5mRz \{je1 ;qSN! H8Mx*]cn 2qq4Amm3d-1.3.15/doc/html/olh_images/tools/sheartool.png000066400000000000000000000040441466047437300221470ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  ~tIME Z IDATxYQQQQQQRIENDB`mm3d-1.3.15/doc/html/olh_images/tools/torustool.jpg000066400000000000000000000014061466047437300222140ustar00rootroot00000000000000JFIFHHExifMM*C  !"$"$C"'!"2$AQ"!A"1 ?P˗ǦjH8jIHz0vfִqKJ, 0"Z]!YY~_s+#3"OwvB<-WdZ%hJ 6K^GVYFF!8n389NOzٺyfQ#NxV uG̍G>9;Fʩ[k}|cF~ um{{OoYf_S'?zqR㨵{.XOnibXI%F;KR;ffbӫ& #z͝+_mm3d-1.3.15/doc/html/olh_images/tools/torustool.png000066400000000000000000000004121466047437300222140ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs  tIME &@4IDAT8U1 K8e:A=2*Ĉ! 6pxKB)p,]Uz4]f8~Y3)7ށ΁vY%)m5$uv$Iݹ鵌A/TՉ& *&o:ME#M(zr-Contents


Overview

Help! What should I read first!?

Here is a short list of the most useful documentation.

  • The Quick Tips page contains a list of things that every regular user should be aware of. If you are impatient and only want to read one document, Quick Tips is the one to read.
  • The Main Window documentation gives you an overview of the user interface and a high-level description of the program's features.
  • See the 3D Model Introduction for information on specific topics.
  • The Tutorial is not yet written. When it is done it will walk you through real-world usage examples for all the essential modeling features.
  • Everything else is reference material for specific topics.

Maverick Model 3D Introduction

Maverick Model 3D is an application that allows users to create and modify triangle-based models. It currently supports a variety of 3D vertex manipulation tools as well as texturing and animations. There is a plugin system so that extensions can be written to provide more tools, commands, or model import and export filters.

Currently Maverick Model 3D has its own file format, but can also export models in the Quake MD2 and MD3, Cal 3D, Milkshape 3D, COB, DXF, OBJ, IQE, SMD, and GameMaker D3D file formats.

When you start Maverick Model 3D you will see the Main Window. The Main Window has a set of viewports that act as a canvas for model manipulation and a tool bar that provides interactive model manipulation features. There can be multiple Main Windows, one for each model that is open.

See the Main Window documentation to get an overview of Maverick Model 3D's user interface as well as links to more specific feature information.

mm3d-1.3.15/doc/html/olh_index.page000066400000000000000000000000601466047437300167730ustar00rootroot00000000000000PAGE_TITLE=Contents PAGE_CONTENT=Influences Menu

Assign Selected to Joint

Overview

Assign Selected to Joint assigns the currently selected vertices and points to the currently selected bone joint. You may have more than one bone joint selected.

Select a bone joint, chose any vertex selection tool, and then use Shift-Select to select vertices without unselecting the bone joint.

Keyboard shortcuts

  • None

Auto-Assign Selected

Overview

Auto-Assign Selected assigns the currently selected vertices and points to the joints that MM3D thinks are most appropriate. You can increase and decrease the tendency to assign a vertex to multiple joints.

Generally auto-assign works fine when the bone joints are obvious, requires some adjustment in weight when near a joint, and doesn't do a very good job at all if the vertex is betwen several joints but not along one of the bone lines. The algorithm is not perfect, but is a good starting point to begin fine-tuning manually.

Keyboard shortcuts

  • None

Remove All Influences from Selected

Overview

Remove All Influences from Selected will unassign any bone joints that are assigned to the selected vertices or points.

Keyboard shortcuts

  • None

Remove Selected Joint from Influencing

Overview

Remove Selected Joint from Influencing will remove the selected bone joint as an influence from any vertex or point.

Keyboard shortcuts

  • None

Convert Multiple Influences to Single

Overview

Convert Multiple Influences to Single will find vertices and points with multiple influences and remove all the influences except for the strongest influence.

Keyboard shortcuts

  • None

Select Joint Influences

Overview

Select Joint Influences will select any bone joints that are assigned as influences to any of the selected vertices or points.

Keyboard shortcuts

  • None

Select Influenced Vertices

Overview

Select Influenced Vertices will select any vertices that are influenced by the selected bone joint.

Keyboard shortcuts

  • None

Select Influenced Points

Overview

Select Influenced Points will select any points that are influenced by the selected bone joint.

Keyboard shortcuts

  • None

Select Unassigned Vertices

Overview

Select Unassigned Vertices will select any vertices that are not influenced any bone joints.

Keyboard shortcuts

  • None

Select Unassigned Points

Overview

Select Unassigned Points will select any points that are not influenced any bone joints.

Keyboard shortcuts

  • None
mm3d-1.3.15/doc/html/olh_influences.page000066400000000000000000000000741466047437300200240ustar00rootroot00000000000000PAGE_TITLE=Influences Menu PAGE_CONTENT=IQE Export Options

The IQE Export Options window is displayed when you save a model in Inter-Quake Export (IQE) format. It allows you to specify things like whether or not to save the bone joints in the file and which animations to save.

The Save as a Quake 3 Player checkbox allows you indicate if you want to export as a Quake 3 composite player model (head.iqe, upper.iqe, and lower.iqe models). If it is grayed out, see the list of requirements for export on the MD3 page.

For exporting as a Quake 3 player, there must be a root joint with a desendent torso joint and desendent (of torso joint) head joint. There must be a "tag_torso" point at the exact location of the torso joint and attached to the torso joint. Same for "tag_head" at exact location of head joint and attached to it. The point names must be "tag_torso" and "tag_head", the joint names can be anything. Do not make the root joint be the torso joint; everything will be a desendent of it and there cannot be leg joints.

Joints/points that are not a desendents of torso joint are exported in lower.iqe. Torso joint and desendent joints/points to (and including) head joint and "tag_head" point are exported to upper.iqe. Head joint and desendent joints/points are exported to head.iqe.

The Save Quake 3 animation.cfg checkbox allows you to indicate if you want to write "animation.cfg" file along side the model. This file contains a list of animations with looping and frames per-second information and can contain other arbitry data from MD3_CFG_* meta data. See the MD3 page for details.

The Save Meshes checkbox allows you indicate if you want the meshes saved in the file or not.

The Save Points as Bone Joints checkbox allows you to indicate if you want the points to be saved as skeleton bone joints or not at all. Points can only have one bone joint influence. "Save Skeleton" or "Save Animations" must also be checked.

The Save Skeleton checkbox allows you to indicate if you want the bone joints saved in the file or not.

The Save Animations checkbox allows you to indicate if you want animations saved in the file or not.

The animations list box allows you to specify which animations you want to be saved in the file.

Click Ok to save the model with the specified options or click Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_iqeprompt.page000066400000000000000000000000761466047437300177130ustar00rootroot00000000000000PAGE_TITLE=IQE Export Options PAGE_CONTENT=Bone Joint Details

Overview

A bone joint is is an object that forms one part of a model's skeletal structure. The root bone joint has no parent joint, all other joints have one parent. A parent joint may have multiple children.

Vertices and points may be attached to bone joints to control their movements during skeletal animations. When a vertex or point's movement is controlled by a bone joint, the bone joint is said to be an "influence". Vertices and points may have up to four influences.

Creating a Bone Joint

Use the Create Bone Joint tool to create a bone joint. If there are no other joints in the model, the new joint will be the root joint. If there are other bone joints in the model, the bone joint nearest to the point where you created the new joint (in 2D space) will be the new joint's parent.

At this time it is not possible to change a bone joint's parent or to insert a bone joint between two existing bone joints.

Other Bone Joint Details

To assign a bone joint as an influence for a vertex (or point), first select the point. Then you can use the Influence Properties panel or Joint Window to select the bone joint for that vertex. You can also use Shift-Select to select a bone joint in addition to the vertex and then use the "Influences | Assign Selected to Joint" command.

If you have more than one bone joint influence on a vertex, you can control how much influence each joint has by changing the weight of that joint in the properties panel. Weights range from 0 to 100. The sum of all influnece weights does not have to equal 100. There are three types of weights:

  • Automatic - The amount of the influnce is determined by the position of the vertex relative to each of the vertex's influences.
  • Custom - The user specifies the amount of the influence by entering the amount directly.
  • Remainder - The amount of influence is the sum of all other weights subtracted from 100 (or zero if the result would be negative).

See Also

mm3d-1.3.15/doc/html/olh_jointdetails.page000066400000000000000000000001011466047437300203510ustar00rootroot00000000000000PAGE_TITLE=Bone Joint Details PAGE_CONTENT=Joints Window

The Joints Window can be opened by selecting Edit Joints... from the Influences menu. The Joints Window allows you to rename bone joints and assign vertices to bone joints. To create bone joints, see the Create Bone Joint tool.

Joints List

The joints combo box lists all the bone joints in the model. Selecting a joint in the combo box will cause that joint to be selected (all other joints will be unselected). After selecting a joint you can rename it, delete it, or assign vertices to it.

Rename

The Rename button allows you to rename a bone joint. Bone joint names do not have to be unique (unless you are exporting to Milkshape 3D format).

Delete

The Delete button deletes a bone joint. The parent joint of the selected joint will become the parent of all the selected joint's child joints. You cannot delete the root joint unless it is the only joint.

Select Joint Vertices

The Select Joint Vertices button selects all the vertices that are assigned to the selected bone joint. All other vertices are unselected.

Select Unassigned Vertices

The Select Unassigned Vertices button selects all the vertices that are not assigned to any bone joint. All assigned vertices are unselected.

Assign Vertcies to Joint

The Assign Vertices to Joint button assigns all the selected vertices to the selected bone joint. Selected vertices that were assigned to another joint will now only be assigned to the new one (when using the Joints Window a vertex may be assigned to only one bone joint, see the Influences Properties Panel if you want to assign a vertex to multiple bone joints). Unselected vertices assigned to the current joint will still be assigned to the current joint.

Press Ok to keep your changes or press Cancel to ignore any changes.

See Also

mm3d-1.3.15/doc/html/olh_jointwin.page000066400000000000000000000000701466047437300175260ustar00rootroot00000000000000PAGE_TITLE=Joints Window PAGE_CONTENT= GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) 19yy <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon> 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mm3d-1.3.15/doc/html/olh_license.page000066400000000000000000000001041466047437300173050ustar00rootroot00000000000000PAGE_TITLE=GNU General Public License PAGE_CONTENT=Main Window

Contents

Overview

The Main Window is where you modify a model. The Main Window has a menu bar, a tool bar, a status bar, and a number of viewports that let you see the model from various directions. There are also several dockable windows. The Properties Panel in particular can be very useful.

There are two modes for the main window. The default one is the Model Edit Mode. This is the mode you are in when you start Maverick Model 3D and are viewing a model. In this mode changes you make affect the position of polygons and bone joints. The other mode is Animation Mode. This mode is active when you have the Animation Panel open. In this mode changes you make affect the current animation frame.

Tool Bar

The Tool Bar has a set of tool buttons. The tools allow you to manipulate a model interactively. For example, you can select the "Draw Cube" tool and then click and drag the mouse in a viewport to create a cube. Each of the tools in the toolbar is also available in the Main Window's Tools menu.

Menu Bar

The menu bar has several menus that provide other functions to modify or create a model.

The File menu allows you to open and save models. There is also an export option to allow you to save in experimental formats. If the file format you want to save as does not appear in the "Save As" dialog, try the Export function. The most recent files you have opened or saved are listed in the "Recent Models" menu. You can load one of these models by selecting the filename from that menu. Additionally, the File menu also allows you to view available plugins.

The View menu allows you to change the number and layout of the viewports in the view window (you may have 1, 2, 4, 6, or 9 viewports). You can force the viewports to show the whole model or selected region by selecing either the Frame All or Frame Selected options, respectively. You can select how to display the model in the othographic (canvas) and perspective viewports based on four options.

  • Wireframe - Shows the model as an outline of all faces.
  • Flat - Shows the model as a set of flat polygons (with lighting).
  • Smooth - Shows the model as a set of smoothed polygons (vertex normals are averaged between all polygons that share them; with lighting).
  • Texture - Shows the model as a set of smoothed, lit polygons with textures applied.
  • Alpha Blend - Shows the model as a set of smoothed, lit polygons with alpha-blended (transparent) textures applied.

There are also some rendering options in the Render Options View submenu. These options include how bone joints are displayed, whether or not texture projections objects are visible, how missing textures are rendered, whether polygon edges (3d lines) are displayed in the perspective views, and whether or not back-facing polygons are drawn.

You can choose how you want bone joints to be displayed. Bones draw a diamond-like object that is thicker toward the parent joint and thinner near the child joint. The Lines option just draws one line between each parent and child joint. The hidden option disables drawing of bone joints.

The Error Texture options indicate how you want unloadable textures to be displayed. The Red Error Texture is a black 'X' on a red background. It is designed to be an obvious indication that textures are missing. If you do not care about missing textures you can use the Blank Error Texture which will render missing textures as if the mesh had no texture applied (material lighting properties will still apply).

The Render/Hide 3D Lines option is used to enable/disable drawing of lines in the perspective viewports. This can be useful if you want to see selected edges in the perspective viewport.

The Draw/Hide back-facing triangles option is used to enable/disable drawing of triangles that are facing away from the viewport camera. By default these are rendered as darkened polygons. In some cases you may want to these polygons to be invisible if they are not facing the camera (such as when using transparent textures).

The Tools menu lists all the available tools. The options in this menu are also listed in the toolbar.

The Model menu has features that affect the model as a whole or large portions of the geometry. It also includes the Undo and Redo options.

Edit Model Meta Data allows you to attach user-defined text values to the model as key/value pairs. In general these have no influence on the model or its geometry, the are just a place for you to store arbitrary information (such as author/artist information, copyright, URLs, etc.).

Transform Model allows you to apply a transformation Matrix to the model. It opens a tab window with different types of transformations. There are tabs for translation, rotation, and scaling, as well as a tab for a user-defined matrix. In some cases the Matrix you want to apply may not be undo-able. If this is the case, the program will warn you before performing the transformation. See the Transform Model Window for more details.

Boolean Operation allows you to combine two object into one. See the Boolean Operation Window for more details.

Set Background Image adds a background image to one or more of the six pre-defined orthographic projections. Background images can be useful as a modeling reference when attempting to create complex shapes. See the modeling tutorial for more information.

Merge imports the geometry, materials, and animations from another model file into the current model. See the Merge Window documentation for details.

Import Animations imports the animations from another model file into the current model. The skeletons of the two models must match.

The Geometry menu lists non-interactive operations that modify model geometry.

The Materials menu lists non-interactive operations that modify groups and materials. See Material Details for more information.

The Influences menu includes functionality for dealing with bone joints and how they influence the model geometry. See the Influences Menu and Bone Joint Details for more information.

The Animations menu provides functions to manipulate model animations. There are interactive and non-interactive functions provided by this menu. See the Animation Panel and Animation Details for more information.

The Help menu brings you to this documentation.

Status Bar

The status bar contains text (on the left side) that describes the results of recent operations on the model. On the right side it shows you how many vertices, faces, groups, bone joints, points, and materials exist in the current model. If you have any vertices, faces, bone joints, or points selected the count will be shown with the selected separated from the total with a slash ('/').

Viewports

The Viewports in the center of the Main Window are where you view and modify a model. There are two types of viewport views: Perspective Views and Orthographic Views. In this documentation orthographic views are sometimes called canvas views because those are the views in which you can use tools to change the model (like a drawing canvas in a 2D paint program). The view type and direction is indicated in a combo box above the viewport on the left. The field of view is indicated in a text entry box above the viewport to the right of the view direction. Larger numbers mean you can see more of the model canvas (think of the opposite of zoom). Three short colored lines indicate the origin. Each color extends in the positive direction along an axis. The red line extends along the X axis, the green line extends along the Y axis, and the blue line extends along the Z axis.

Ortho Wireframe

Ortho wireframe

Ortho Filled

Ortho filled

Persp Wireframe

Persp wireframe

Persp Filled

Persp filled

You can scroll the active viewport by clicking on the arrows in the upper-right corner. The four-way arrow allows you to click and drag the viewport. You can also use the middle mouse button to drag the viewport. You can zoom in and out by using the magnifying glass buttons above the viewport scroll buttons.

Holding the Ctrl key and clicking on the pan arrows in the corner will rotate the viewport. Holding Ctrl and using the mouse scroll wheel will also rotate the viewport.

The view type and direction can be changed to see the model from various angles. In the Perspective View near portions of the model appear larger, far portions of the model appear smaller. The Perspective View may also have visible lighting and texture effects. This view only allows you to view the model. You cannot modify the model from a viewport in Perspective mode.

By left-clicking and dragging your mouse pointer you can rotate the perspective view along the horizontal axis or vertical axis. By middle-clicking and dragging you can pan the viewport left, right, up, and down. Using the scroll wheel will zoom in or out. The right mouse button has no effect in the Perspective View.

The Orthographic Views show the model from one of six directions: Front, Back, Left, Right, Top, or Bottom. These views are a CAD-like projection of the model from the specified direction. These views are not modified by perspective. Two cubes of equal size will always appear the same size in this view regardless of how "far" they are from the viewing plane. Each of the six orthographic views is a canvas view, where you can create and manipulate model vertices and faces. The othrographic viewports may also render the model as a wireframe, solid-flat polygons, solid-smooth polygons, or textured polygons.

An orthographic viewport can be rotated by holding the Ctrl button and click-dragging with the left mouse button. You can toggle a viewport between orthographic and perspective by pressing the Left Quote (Backtick) key.

If you want to be able to save your current viewport settings (pan, zoom, and rotation) and restore the settings at a later time, you can press Ctrll+[Num] where [Num] is a number key from 0 to 9. To restore the viewport, press the [Num] key.

The effect of the left and right mouse buttons depends on the tool that is currently selected. The left mouse button is the main tool button and the right button is an alternate button. For example, using the select tool you can select vertices and faces with the left mouse button and unselect them with the right mouse button. By middle-clicking and dragging you can drag the viewport left, right, up, and down. Using the scroll wheel will zoom in or out.

The active viewport (the viewport under the mouse) will have a slightly lighter background color than the others. Some keyboard shortcuts will modify the selected viewport. For example + and - will zoom in and out. 0 (zero) will center the viewport on the origin. The backslash will flip an orthographic view to the opposite side (front view will become a back view). The backtick (left quote) will switch an orthographic view to a perspective view.

mm3d-1.3.15/doc/html/olh_mainwin.page000066400000000000000000000000651466047437300173330ustar00rootroot00000000000000PAGE_TITLE=Main Window PAGE_CONTENT=Material Details

Overview

A material defines how light reflects off of faces in a group. This includes the color of the faces and a texture map. A group can have only one material applied. The same material may be applied to more than one group.

Creating a Material

A material can be created using the Materials Window. You can open the Materials Window from the Materials menu, or by selecting one or more faces and then clicking the "..." button under "Group Materials" (note that you must have a group assigned to the faces for this to work.

Once you have opened the Materials Window, click "New Material..." and enter a name for the material. If you want to apply a 2D image texture map, click the "Set Texture" button.

Note that textures filenames in MM3D are saved relative to the directory in which the model is saved. It is a good idea to copy your image into the model directory or a sub-directory from where the model is.

If you are editing a texture file with a paint program while MM3D is running you can reload the texture using the "Reload Textures" option in the Materials menu.

Other Material Details

When changing the material that is applied to faces using the Group Properties panel, be aware that changing the material for the selected faces applies this change to all faces in the group, not just the selected ones.

To change the way the texture is mapped onto the faces of the group you will need to move the Texture Coordinates. Sometimes using a Texture Projection gives you a good starting point for texture coordinate assignment.

See Also

mm3d-1.3.15/doc/html/olh_materialdetails.page000066400000000000000000000001021466047437300210250ustar00rootroot00000000000000PAGE_TITLE=Material Details PAGE_CONTENT=Merge Window

Overview

The Merge Window allows you to merge a model file into the current model. After selecting a model file you will be prompted with the merge window to specify the location of the new model in the existing model as well as options for importing textures and animations.

Merge Location

The merge location frame is made up of three components, the Base Point, Rotation, and Translation components.

The Base Point specifies whether to use the origin or a point in the existing model as the base rotation and translation for the new model. It's particularly useful for merging a model into existing frame animations.

The Rotation specifies how the new model will be rotated relative to the base point in the existing model. You can rotate the new model on all three axes. Rotation is specified in degrees.

The Translation specifies the location of the new model relative to the base point of the existing model. The translation is specified in GL units. The relative position will depend on the scale of your model.

Merge Options

The Include textures checkbox allows you to specify if textures from the new model should be merged into the existing model.

The Include animations checkbox allows you to specify if animations from the new model should be merged into the existing model. If this checkbox is selected, you can also specify Animation Options

Animation Options

The Append animations checkbox will make the animations of the existing model and animations of the new model separate animations. Both models will appear in each others animations, but only one will animate. This only applies to the merge. After the models are merged you may modify the existing animations to animate both models.

The Merge if possible checkbox will attempt to combine the existing animations of both models so that both models are animated in each animation. In order to merge animations you must have the same number of animations in each model and the frame counts of the corresponding animations must also match (if model A has animations with 10, 15, and 12 frames, and model B has animations with 10, 15, and 12 frames they can be merged). Skeletal and Frame animations are merged independently. If you have a model which contains both types of animations and one type can be merged and the other cannot, the mergeable type will be merged.

Note that you can merge skeletal animations using the Animation Sets window even after the model has been merged. Frame animations cannot be merged through the Animations Sets window.

Press Ok to merge the new model into the current model or press Cancel to abort the merge.

mm3d-1.3.15/doc/html/olh_mergewin.page000066400000000000000000000000671466047437300175100ustar00rootroot00000000000000PAGE_TITLE=Merge Window PAGE_CONTENT=Mesh Details

Overview

A mesh is a collection of triangles that are connected by common vertices. If every edge of every triangle in the mesh is connected to another triangle in the mesh (there are no gaps or holes in the mesh) the mesh is called an "enclosed mesh".

Meshes are not first-class objects to Maverick Model 3D, but there are some operations where use of the term mesh can be helpful.

Note that the term "mesh" is distinct from "group". The term "group" in MM3D generally refers to a collection of triangles that have been defined as a group and may have a material applied. Triangles in a mesh may or may not be in a group.

Creating a Mesh

Meshes are created when you use the creation tools to create geometric shapes such as cubes, spheres, or cylinders.

Other Mesh Details

You can combine meshes using the Boolean Operataions panel. For the union, intersection, and subtraction operations to work properly, all meshes involved must be enclosed (otherwise the behavior of the face-removal step is undefined). If the meshes are not enclosed you can still use the fuse operation and manually remove the faces that must be removed.

The Simplify Mesh command is useful for combining faces that do not add detail to a shape. For example if you have a cube where each side is made up of 8 triangles, then all of these triangles are in the same plane and many edges form a single straight line. In this case, the eight faces on each side can be reduced to two faces. Often when you use a boolean operation to combine two objects you will want to use the simplify mesh command to eliminate unecessary faces from the model.

If you have mesh that is not enclosed you can use the Cap Holes command to create faces to fill in the gaps. Note that the cap holes feature is very limited. If your shape is relatively complex it may have difficulty correctly determining how to connect faces to fill in the gaps.

See Also

mm3d-1.3.15/doc/html/olh_meshdetails.page000066400000000000000000000000721466047437300201710ustar00rootroot00000000000000PAGE_TITLE=Mesh Details PAGE_CONTENT=Model Meta Data Window

The Model Meta Data Window is used to add or remove information associated with a model that does not affect rendering. This can include any arbitrary text data such as the name of the creator, copyright information, URLs, etc.

The main component of the window is the Name/Value list box. This list box contains name and value pairs.

To add a new name/value pair click the New button. A new Name/Value row will appear. You can edit the name and value by pressing the Enter key or by double-clicking on the name or value with the mouse. Names do not need to be unique, though duplicate names may be confusing for other people using this model.

To remove a name/value pair from the model, select the name in the list box and click the Delete button.

Press Ok to keep your changes or press Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_metawin.page000066400000000000000000000001001466047437300173230ustar00rootroot00000000000000PAGE_TITLE=Model Meta Data Window PAGE_CONTENT= MM3D File Format

Contents

Overview

This document describes version 1.7 of the MM3D (Misfit Model 3D) file format. Data that is available only in specific versions will be noted below. All other data is common to every version of the file format. Previous releases of Misfit Model 3D and Maverick Model 3D that do not read all data segments should still be able to load model files, though some information may be lost.

The MM3D file format was designed to be easy to extend and easy to read to extract just the information you want. The header will tell you what information is included in the file and give you offsets to the location where the information is in the file.

Data sections have their own header before the data. Each data type is either a fixed size or a variable size. If the data size is fixed (constant, Type B) the data size immediatley precedes the data block of the data section. If the data size is variable (Type A) the data size precedes each data element.

MM3D uses Intel byte order (least significant byte first). Bytes are 8 bits. The types specified in the Type fields below are ints (signed integers) or uints (unsigned integers). The type designation is followed by a number which designates the number of bits in that integer. The type may be multiplied by a constant or variable number to get the full length of the field. Variables are generally data fields from earlier sections in the file. In the data chunks there is also a float32 which is a 32-bit floating point number and an ASCIIZ which is every byte up to and including the next null byte. ASCIIZ strings are truncated to 1024 bytes (including the terminating null) if necessary.

As of MM3D version 1.4 all text strings are encoded as UTF-8. Versions 1.2 and earlier used extended ASCII encoding. If you want your MM3D models to be usable in new and older versions of MM3D you should use only standard ASCII characters (0-127) for model object names and texture filenames.

File Header

The file header is 12 bytes.

Data Type Notes Description
MAGIC_NUMBER int8 * 8 'MISFIT3D' If the first 8 bytes do not match this string it is not an MM3D file
MAJOR_VERSION uint8 0x01 Major version number, the file format may be incompatible with newer or older filters if the major version number does not match
MINOR_VERSION uint8 0x07 Minor version number, older filters should be able to get useful data from the file but may not understand the value of all flags or data segments
MODEL_FLAGS uint8 0x00 (reserved) Currently there are no model-wide flags, this byte is reserved in case such flags are ever needed
OFFSET_COUNT uint8   This is the number of data segments. It tells you how many offsets are in the next section of the file

Data Offsets

Data offsets are 6 bytes each. The value of [OFFSET_COUNT] in the header specifies how many data offsets are in this section.

The data offsets section is [OFFSET_COUNT] instances of:

Data Type Description
OFFSET_TYPE uint16 Type of data for this offset (types listed in the next section)
OFFSET_VALUE uint32 Offset to the data segment for this type (offset is from the start of the file)

Data Offset Types

The following is a list of known data segment types. Types are 16 bits. Bit 15 (0x8000) is a "uniform" flag, meaning data of that type is a fixed (constant) size.

Bit 14 (0x4000) is the dirty flag. If Mifit Model 3D encounters a data segment it does not understand it will keep a copy of the raw data and write that data to a file if the model is saved again. It will add the dirty bit to indicate to other model readers that this data was written by a version of the program that did not understand the data. It is therefore "dirty" and may be inconsistent with the model. Under these circumstances the reader may chose to discard the data or attempt to verify and use it.

The end of file offset is a special offset and indicates the file size. You should always check this value against the file size to be certain that the file is complete. There is no data at the end of file offset.

Type A (variable)

Type A Version Description
0x1001 1.4+ Meta data
0x1002   Unknown type information (not implemented)
0x0101   Groups
0x0141   Embedded textures (not implemented)
0x0142   External textures
0x0161   Materials
0x016c 1.6+ Texture Projections Triangles
0x0191 1.4+ Canvas Background Images
0x0301   Skeletal Animations
0x0321   Frame Animations
0x0326 1.6+ Frame Animation Points
0x0341   Frame Relative Animations (not implemented)
0x3fff   End of file (offset is file size)

Type B (fixed)

Type B Version Description
0x8001   Vertices
0x8021   Triangles
0x8026   Triangle Normals
0x8041   Joints
0x8046   Joint Vertices
0x8061 1.6+ Points
0x8106 1.4+ Smoothness Angles
0x8146 1.6+ Weighted Influences
0x8168 1.6+ Texture Projections (sphere/cylinder map)
0x8121   Texture Coordinates

User Defined Blocks

Types 0x0000 through 0x1fff are reserved for Maverick Model 3D defined types. 0x4000 through 0xffff are reserved for the uniform and dirty bit versions of each type. If you want to add a type for your own use it is recommended that you use a value in the 0x2000 through 0x2fff range. Suggestions for types which may be useful to other uses are welcome and may be incorporated into future versions.

Data Blocks

Data Blocks have a header and a data chunk. The data blocks are similar between types A and B. The only difference is how the size of each data element is handled. In Type A the size is included before each data element in the data chunk. In Type B the size is included at the end of the header and before the data chunk itself.

The size of a data element may change. When reading data from an MM3D file it is important to note if the size of the element is different from the expected size. If it is smaller, data at the end of the element has been omitted. If larger, data has been appended to the end of the element's data type and must be skipped over during data parsing. These situations may happen if your filter is reading a file written by a newer or older version of Maverick Model 3D. Data size changes will be rare but should be accounted for.

Type A

Data header:

Data Type Description
DATA_FLAGS uint16 Flags for the data segment (currently unused, should be 0x0000)
DATA_COUNT_A uint32 The number of elements in each data chunk

Data chunk:

Data Type Description
DATA_SIZE_A uint32 Size of this element of data
DATA_CHUNK_A uint8 * [DATA_SIZE_A] One element of [DATA_SIZE_A] size, Data Block Types are defined below

Type B

Data header:

Data Type Description
DATA_FLAGS uint16 Flags for the data segment (currently unused, should be 0x0000)
DATA_COUNT_B uint32 The number of elements in each data chunk
DATA_SIZE_B uint32 Size of each element of data

Data chunk:

Data Type Description
DATA_CHUNK_B uint8 * [DATA_SIZE_B] * [DATA_COUNT_B] [DATA_COUNT_B] elements of [DATA_SIZE_B] size, Data Block Types are defined below

Data Block Types

Meta Data

Version 1.4 and later.

The meta data section contains key/values pairs of ASCIIZ strings. These key/value pairs do not affect model display in any way. These are purely informational strings for things like copyright, contact, URLs, etc.

Keys are case-sensitive and do not have to be unique (though having two identical keys is bad form).

Data Version Type Description
META_KEY 1.4+ ASCIIZ Key for this meta data key/value pair
META_VALUE 1.4+ ASCIIZ Value associated with meta data key

Vertices

Data Type Description
VERTEX_FLAGS uint16 See vertex flags below
VERTEX_COORD_X float32 X coordinate of the vertex
VERTEX_COORD_Y float32 Y coordinate of the vertex
VERTEX_COORD_Z float32 Z coordinate of the vertex

Vertex flags:

Bits Version Data Description
0   Hidden Set if hidden, clear if visible
1   Selected Set if selected, clear if unselected
2 1.6+ Free Vertex Set if vertex does not have to be connected to a face (don't auto-delete when face is deleted)

Triangles

The triangles section describes the triangular faces of the model. The vertex indices are 0-based indices into the vertex array from the vertices section.

Data Type Description
TRIANGLE_FLAGS uint16 See triangle flags below
TRIANGLE_VERTEX_1 uint32 Index of vertex 1 of the triangle
TRIANGLE_VERTEX_2 uint32 Index of vertex 2 of the triangle
TRIANGLE_VERTEX_3 uint32 Index of vertex 3 of the triangle

Triangle flags:

Bits Data Description
0 Hidden Set if hidden, clear if visible
1 Selected Set if selected, clear if unselected

Triangle Normals

Version 1.4 and later.

The triangle normals section describes the normals for each face of the model. Each block is composed of a triangle index that indicates to which triangle the normals apply to, followed by three sets (one for each triangle vertex) of three floating point numbers (one for each dimension).

Data Version Type Description
TRI_NORM_FLAGS 1.4+ uint16 Reserved, should be 0
TRI_NORM_INDEX 1.4+ uint32 Triangle index (0 based)
TRI_NORM_V1_NORM 1.4+ float32 * 3 Normals for vertex 1 (XYZ)
TRI_NORM_V2_NORM 1.4+ float32 * 3 Normals for vertex 2 (XYZ)
TRI_NORM_V3_NORM 1.4+ float32 * 3 Normals for vertex 3 (XYZ)

Joints

The joint section describes joints used for skeletal animations. Their orientation information is relative to their parent joint's orientation.

Data Type Description
JOINT_FLAGS uint16 See joint flags below
JOINT_NAME int8 * 40 Joint name
JOINT_PARENT int32 Index of parent joint (or -1 if root joint)
JOINT_LOCAL_ROT_X float32 X rotation of joint relative to parent (in radians)
JOINT_LOCAL_ROT_Y float32 Y rotation of joint relative to parent
JOINT_LOCAL_ROT_Z float32 Z rotation of joint relative to parent
JOINT_LOCAL_TRANS_X float32 X offset of joint relative to parent (or origin if root joint)
JOINT_LOCAL_TRANS_Y float32 Y offset of joint relative to parent
JOINT_LOCAL_TRANS_Z float32 Z offset of joint relative to parent

Joint flags:

Bits Data Description
0 Hidden (ingored) Set if hidden, clear if visible
1 Selected Set if selected, clear if unselected

Joint Vertices

The joint vertices section associates vertices with joints.

NOTE: As of version 1.6 this data is deprecated. Use the Weighted Influences data instead. This data section is maintained for backwards compatibility. Newer files should have this data and the Weighted Influences data. If the Weighted Influences section is present it takes precedence over this data. When writing this data, only one joint per vertex is allowed. The joint with the most influence (highest weight) on the vertex is the joint index that should be used.

Data Type Description
VERTEX_INDEX uint32 Index into vertex array
JOINT_INDEX uint32 Index into joint array for this vertex

Points

Version 1.6 and later.

Points are objects that have a position and orientation. They can be attached to bone joints for animation purposes. Points do not affect model geometry in any way. They are simply reference objects for specifying a location in the model. One potential use for this is bolt points for attaching one model to another (such as tags in MD3 models).

Note that the POINT_JOINT element is deprecated. It should still be set in MM3D files (or -1 if no bone joint assignment). If the point is assigned to multiple bone joints, the joint with the most influence (highest weight) should be used. When reading MM3D files, the POINT_JOINT element should usable, but any data in the Weighted Influences section takes precedence over this value.

Data Type Description
POINT_FLAGS uint16 See point flags below
POINT_NAME int8 * 40 Point name
POINT_TYPE int32 Type of point (reserved, should be zero)
POINT_JOINT int32 Index of parent joint (deprecated, use Weighted Influences)
POINT_ROT_X float32 X rotation of point (in radians)
POINT_ROT_Y float32 Y rotation of point
POINT_ROT_Z float32 Z rotation of point
POINT_TRANS_X float32 X position of point
POINT_TRANS_Y float32 Y position of point
POINT_TRANS_Z float32 Z position of point

Point flags:

Bits Data Description
0 Hidden (ingored) Set if hidden, clear if visible
1 Selected Set if selected, clear if unselected

Smoothness Angles

Version 1.4 and later.

Defines the maximum angle at which triangle edges in a group will be averaged for normal calculation. If two triangles share an edge that is greater than this angle, the edge will appear sharp rather than rounded.

Data Version Type Description
GROUP_INDEX 1.4+ uint32 Index into group array
ANGLE 1.4+ uint8 Maximum angle (in degrees) to use in smoothing normals (0-180)

Groups

The groups section groups faces (triangles) for editing or texturing purposes. Textures are applied to groups, so faces must be grouped in order to have a texture applied.

Data Type Description
GROUP_FLAGS uint16 See group flags below
GROUP_NAME ASCIIZ Name of the group of faces
TRIANGLE_COUNT uint32 Number of triangles in the group
TRIANGLE_INDICES uint32 * [TRIANGLE_COUNT] Index of each triangle belonging to the group
GROUP_SMOOTHNESS uint8 Determines how the face normals are interpolated between faces. 0 = flat along face plane, ff=averaged between all planes that share the vertex
GROUP_MATERIAL uint32 Index into material list (0xffffffff for none)

Group flags:

Bits Data Description
0 Hidden (ingored) Set if hidden, clear if visible
1 Selected Set if selected, clear if unselected

Weighted Influences

Version 1.6 and later.

The weighted influences section describes how vertices and points are assigned to bone joints. Objects can be assigned to multiple bone joints. This is the preferred method to save and load bone joint assignments for vertices and points. New versions of Maverick Model 3D will use this data if it is present and the deprecated data sections if this data is not present.

The type of influence can be automatic (based on the position of the object relative to the bone joint), remainder (100 minus the sum of all other joint weights), or custom (manually entered by the user). If the type is "Remainder" and the sum of the influence of other joints is greater than 100, the remainder value is 0 (it is never negative).

The weight of each influence ranges from 0 to 100. The relative influence of each joint is the percentage of the total influence of all joints. The sum off all influence weights does not have to be 100. For example a single joint with a weight of 80 will have 100% influence. Two joints that each have a weight of 80 will each have 50% influence. One joint with a weight of 40 and another with a weight of 80 will have 33% and 67% influence respectively.

Data Type Description
POS_TYPE uint8 Type of object influenced by joint (see position types below)
POS_INDEX uint32 Index into vertex or point array (based on position type)
INF_INDEX uint32 Bone joint index
INF_TYPE uint8 Type influence by joint (see influence types below)
INF_WEIGHT int8 Weight of this joint's influence (0 to 100)

Position Types:

Number Version Type
0 1.6+ Vertex
2 1.6+ Point

Influence Types:

Number Version Type
0 1.6+ Custom (manually entered by user)
1 1.6+ Automatic
2 1.6+ Remainder

Texture Projections

Version 1.6 and later.

The texture projection section describes objects that map triangle texture coordinates onto a texture as a cylinder or sphere. The "up" vector defines which direction is the U component of the texture projection. In some cases (such as cylinder) the magnitude of this vector determines the size of the projection area. The "seam" vector indicates where the V texture component starts and 0.0 and wraps back to 0.0 after reaching 1.0.

Data Type Description
PROJ_FLAGS uint16 See projection flags below
PROJ_NAME int8 * 40 Projection name
PROJ_TYPE int32 Type of Projection (see types below)
PROJ_POS_X float32 X coordinate of projection center
PROJ_POS_Y float32 Y coordinate of projection center
PROJ_POS_Z float32 Z coordinate of projection center
PROJ_UPVEC_X float32 X component of up vector
PROJ_UPVEC_Y float32 Y component of up vector
PROJ_UPVEC_Z float32 Z component of up vector
PROJ_SEAMVEC_X float32 X component of seam vector
PROJ_SEAMVEC_Y float32 Y component of seam vector
PROJ_SEAMVEC_Z float32 Z component of seam vector
PROJ_MIN_U float32 Minimum U texture coordinate
PROJ_MIN_V float32 Minimum V texture coordinate
PROJ_MAX_U float32 Maximum U texture coordinate
PROJ_MAX_V float32 Maximum V texture coordinate

Projection flags:

None

Projection types:

Number Version Type
0 1.6+ Cylinder
1 1.6+ Sphere

Texture Coordinates

The texture coordinates section maps the vertices of a triangle onto a material's texture. Texture coordinates range from (0.0, 0.0) to (1.0, 1.0). The origin (0,0) of a texture is the lower-left corner, not the upper-left. Values outside of the 0.0 to 1.0 range are valid.

Data Type Description
TEXCOORD_FLAGS uint16 Unused
TEXCOORD_TRIANGLE uint32 Triangle for this texture coordinate set
TEXCOORD_VERTEX_1_S float32 S (x) coordinate for triangle vertex 1
TEXCOORD_VERTEX_2_S float32 S (x) coordinate for triangle vertex 2
TEXCOORD_VERTEX_3_S float32 S (x) coordinate for triangle vertex 3
TEXCOORD_VERTEX_1_T float32 T (y) coordinate for triangle vertex 1
TEXCOORD_VERTEX_2_T float32 T (y) coordinate for triangle vertex 2
TEXCOORD_VERTEX_3_T float32 T (y) coordinate for triangle vertex 3

External textures

The external textures section describes the textures in the model. The textures are assigned to materials which describe the lighting properties. Groups reference materials. One texture may be referenced by many materials.

Before Maverick Model 3D 1.3.13, the external textures count was incorrectly written as material count. It may be higher than the number of external textures written in the file. When loading the file you could check if external textures overlaps with Texture Coordinates which was always written directly afterward.

Data Type Description
TEXTURE_FLAGS uint16 Flags for texture (unused)
TEXTURE_PATH ASCIIZ File path to texture relative to model (directory separator is backslash)

Materials

The materials section describes the materials in the model. The materials reference textures and describe the lighting properties. Groups reference materials. Some materials do not have textures. For materials without textures, the MATERIAL_TEXTURE element is still present but the value is ignored. The MATERIAL_FLAGS element indicates if the material has a texture (see below).

Data Type Description
MATERIAL_FLAGS uint16 See material flags below
MATERIAL_TEXTURE uint32 Texture index for this material
MATERIAL_NAME ASCIIZ Name of the material
MATERIAL_AMBIENT float32 * 4 Ambient light properties (RGBA)
MATERIAL_DIFFUSE float32 * 4 Diffuse light properties (RGBA)
MATERIAL_SPECULAR float32 * 4 Specular light properties (RGBA)
MATERIAL_EMISSIVE float32 * 4 Emissive light properties (RGBA)
MATERIAL_SHININESS float32 Shininess

Material flags:

The Clamp S and Clamp T values specify whether texture coordinates outside of the 0.0 to 1.0 range should wrap or not. If the bit is set, the texture wraps. If the bit is clear the colors at the edge of the texture are stretched.

Bits Version Data Description
0-3   Material type 0 = external texture, 15 = Blank (no texture, only lighting), 1-14 = reserved
4 1.4+ Clamp S Clamp S texture coordinates (do not tile/repeat)
5 1.4+ Clamp T Clamp T texture coordinates (do not tile/repeat)

Texture Projection Triangles

Version 1.6 and later.

The texture projection triangles section is a list of which triangles are using a specified texture projection to set their texture coordinates.

Data Type Description
PROJ_INDEX uint32 Texture Projection Index to which these triangles are assigned
TRI_COUNT uint32 Number of triangles assigned to this projection
TRI_INDICES uint32 * [TRI_COUNT] List of triangle indices that are assigned to projection

Canvas Background Images

Version 1.4 and later.

The Canvas Background Image section describes background images that appear in canvas views. There can be one background image for each of the six canvas views (Front, Back, Left, Right, Top, Bottom) and each can be scaled and positioned independently. Canvas background images are for editing purposes only and should not be rendered as part of the model itself.

Data Version Type Description
CANVBACKIMAGE_FLAGS 1.4+ uint16 Reserved, should be 0
CANVBACKIMAGE_VIEWINDEX 1.4+ uint8 View direction (see below)
CANVBACKIMAGE_SCALE 1.4+ float32 Image scale (1.0 = center to top/left is 1.0 OpenGL unit)
CANVBACKIMAGE_CENTER 1.4+ float32 * 3 Center coordinates of image
CANVBACKIMAGE_FILENAME 1.4+ ASCIIZ Filename of the image to load (relative path)

Canvas Background Image View Directions

View Index Version Direction
0 1.4+ Front
1 1.4+ Back
2 1.4+ Left
3 1.4+ Right
4 1.4+ Top
5 1.4+ Bottom

Skeletal Animations

The skeletal animations section describes the skeletal animations in the model. It includes all frames of all skeletal animations. This mostly consists of animation data and joint keyframes.

Data Type Description
SKEL_FLAGS uint16 See skeletal animation flags below
SKEL_NAME ASCIIZ Name of animation
SKEL_FPS float32 Frames per second
SKEL_FRAME_COUNT uint32 Number of frames in animation
SKEL_FRAME_DATA SKEL_FRAME_TYPE * [SKEL_FRAME_COUNT] Frame data for skeletal animation, see below

Skeletal animation flags:

Bits Version Data Description
0 1.7+ Loop If set it's a looping aniamtion, if clear it's non-looping

Skeletal frame data type:

Data Type Description
SKEL_KEYFRAME_COUNT uint32 Number of joint keyframes for this skeletal animation frame
SKEL_KEYFRAME_DATA SKEL_KEYFRAME_TYPE * [SKEL_KEYFRAME_COUNT] Keyframe data for this frame, see below

Skeletal keyframe data type:

Data Type Description
SKEL_KEYFRAME_JOINT uint32 Joint that this keyframe is for
SKEL_KEYFRAME_ANIM_TYPE uint8 Keyframe type: 1 = translation, 0 = rotation
SKEL_KEYFRAME_POS_X float32 X position data (translation or rotation [in radians])
SKEL_KEYFRAME_POS_Y float32 Y position data
SKEL_KEYFRAME_POS_Z float32 Z position data

Frame Animations

The frame animations section describes the frame animations in the model. It includes all frames of all frame animations. This mostly consists of animation data and vertex positions for each animation frame (each frame is vertex positions for every vertex in the model).

Data Type Description
FRAME_FLAGS uint16 See frame animation flags below
FRAME_NAME ASCIIZ Name of animation
FRAME_FPS float32 Frames per second
FRAME_COUNT uint32 Number of frames in animation
FRAME_DATA FRAME_ANIM_DATA * [FRAME_COUNT] Vertex position data for each frame, see below

Frame animation flags:

Bits Version Data Description
0 1.7+ Loop If set it's a looping aniamtion, if clear it's non-looping

Frame animation frame data type:

Data Type Description
FRAME_VERTEX_DATA FRAME_VERTEX_TYPE * [VERTEX_COUNT] Vertex position data for each vertex in this frame, see below

Frame animation vertex data type:

Data Type Description
FRAME_VERTEX_COORD_X float32 X coordinate for vertex
FRAME_VERTEX_COORD_Y float32 Y coordinate for vertex
FRAME_VERTEX_COORD_Z float32 Z coordinate for vertex

Frame Animation Points

Version 1.6 and later.

The frame animations points section describes the location and rotation of points in frame animations.

Data Type Description
FPOINT_FLAGS uint16 Flags for skeletal animation (unused)
FPOINT_ANIM uint32 Index of frame animation
FPOINT_FRAME_COUNT uint32 Number of frames that follow
FPOINT_POINT_DATA FPOINT_FRAME_DATA * [FPOINT_FRAME_COUNT] * [POINT_COUNT] Point position and rotation data for each frame, see below

There is one of these data structures for each point for each animation frame. Frame animation point data type:

Data Type Description
FPOINT_ROT_X float32 X rotation of point (in radians)
FPOINT_ROT_Y float32 Y rotation of point
FPOINT_ROT_Z float32 Z rotation of point
FPOINT_TRANS_X float32 X position of point
FPOINT_TRANS_Y float32 Y position of point
FPOINT_TRANS_Z float32 Z position of point

Example Hex Dump

This is an example hex dump of a cube with grouped faces. It is color-coded to highlight the different parts of the file.

Key:

  • File header green
  • Offsets blue
  • Vertices dark red
  • Faces magenta
  • Groups light red
0000000: 4d49 5346 4954 3344 0101 0004 0180 2400 MISFIT3D......$.
0000010: 0000 2180 9e00 0000 0101 5001 0000 ff3f ..!.......P....?
0000020: a001 0000 0000 0800 0000 0e00 0000 0200 ................
0000030: 0000 803f 0000 80bf 0000 803f 0200 0000 ...?.......?....
0000040: 80bf 0000 80bf 0000 803f 0200 0000 803f .........?.....?
0000050: 0000 803f 0000 803f 0200 0000 80bf 0000 ...?...?........
0000060: 803f 0000 803f 0000 0000 80bf 0000 80bf .?...?..........
0000070: 0000 80bf 0000 0000 803f 0000 80bf 0000 .........?......
0000080: 80bf 0000 0000 80bf 0000 803f 0000 80bf ...........?....
0000090: 0000 0000 803f 0000 803f 0000 80bf 0000 .....?...?......
00000a0: 0c00 0000 0e00 0000 0000 0300 0000 0100 ................
00000b0: 0000 0000 0000 0000 0000 0000 0200 0000 ................
00000c0: 0300 0000 0000 0700 0000 0500 0000 0400 ................
00000d0: 0000 0000 0400 0000 0600 0000 0700 0000 ................
00000e0: 0000 0200 0000 0000 0000 0500 0000 0000 ................
00000f0: 0500 0000 0700 0000 0200 0000 0000 0600 ................
0000100: 0000 0400 0000 0100 0000 0000 0100 0000 ................
0000110: 0300 0000 0600 0000 0000 0700 0000 0600 ................
0000120: 0000 0300 0000 0000 0300 0000 0200 0000 ................
0000130: 0700 0000 0000 0000 0000 0100 0000 0400 ................
0000140: 0000 0000 0400 0000 0500 0000 0000 0000 ................
0000150: 0000 0100 0000 4600 0000 0000 6772 6f75 ......F.....grou
0000160: 7020 6e61 6d65 000c 0000 0000 0000 0001 p name..........
0000170: 0000 0002 0000 0003 0000 0004 0000 0005 ................
0000180: 0000 0006 0000 0007 0000 0008 0000 0009 ................
0000190: 0000 000a 0000 000b 0000 00ff ffff ffff ................
mm3d-1.3.15/doc/html/olh_mm3dformat.page000066400000000000000000000000751466047437300177430ustar00rootroot00000000000000PAGE_TITLE=MM3D File Format PAGE_CONTENT=MS3D Export Options

The MS3D Export Options window is displayed when you save a model in MS3D format. It allows you to specify how you want bone joint influences to be saved.

The Vertex Format options specify which MS3D vertex subversion you want to save as. If no vertices in your model are assigned to more than one bone joint, select Subversion 0. Otherwise, select Subversion 1, 2, or 3.

  • Subversion 0 - Original file format, a vertex can only be assigned to one bone joint.
  • Subversion 1 - A vertex may be assigned to multiple bone joints, the weight range is 0 to 255. Models in this format do not render correctly in MilkShape 3D 1.8.0 and later.
  • Subversion 2 and 3 - A vertex may be assigned to multiple bone joints, the weight range is 0 to 100.

The Subversion Options section includes options that only apply to specific subversions. The Vertex Extra option is a 32-bit number that is saved with the vertex data in Subversion 2 and 3. The Vertex Extra 2 option is a 32-bit number that is saved with the vertex data in Subversion 3. The numbers are specified in hexadecimal. The numbers supplied is used for all vertices. There is no way to set these values for individual vertices.

Click Ok to save the model with the specified options or click Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_ms3dprompt.page000066400000000000000000000001001466047437300177670ustar00rootroot00000000000000PAGE_TITLE=MS3D Export Options PAGE_CONTENT=Normal Details

Overview

A normal is line that is perpendicular to the plane of a triangle. The normal is used to perform lighting reflection calculations and determine whether a triangle faces toward the camera or away from the camera.

If faces are connected to each other, the normals can be averaged to make the edges where they connect appear smooth. You cannot directly manipulate a face's normal, but you can influence the averaging calculations by assigning faces to a group and then changing the group properties. See the Group Window for details.

You can invert the normals of selected faces using the Invert Normals command. If some or all normals in a mesh are facing the wrong direction (pointing inward) and the faces are part of an enclosed mesh, you can use the Normals Face Out command to make all normals point outward. If the triangles are not part of an enclosed mesh the behavior is undefined.

See Also

mm3d-1.3.15/doc/html/olh_normaldetails.page000066400000000000000000000000761466047437300205310ustar00rootroot00000000000000PAGE_TITLE=Normal Details PAGE_CONTENT=OBJ Export Options

The OBJ Export Options window is displayed when you save a model in OBJ format. It allows you to specify things like whether or not to save the normals in the file and what level of precision you want (which affects overall file size).

The Save Normals checkbox allows you to indicate if you want the normals saved in the file or not.

The Vertex Decimal Places input box allows you specify the precision of the vertex coordinates. The number shown indicates how many numbers will follow after the decimal point.

The Texture Decimal Places input box allows you specify the precision of the texture coordinates. The number shown indicates how many numbers will follow after the decimal point.

The Normal Decimal Places input box allows you specify the precision of the triangle normals. The number shown indicates how many numbers will follow after the decimal point.

Click Ok to save the model with the specified options or click Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_objprompt.page000066400000000000000000000000761466047437300177070ustar00rootroot00000000000000PAGE_TITLE=OBJ Export Options PAGE_CONTENT=Offset by Normal Window

The Offset by Normal Window is used to interactively move the selected vertices along the vertex normal. This only affects vertices that are connected to a triangle. It is made up of a text entry box for the max distance with a slider and a text entry box for the percent value.

The vertex normal is the averaged direction of connected triangles. It is not affected by the group smoothing and max angle.

The offset to move the selected vertexes is calculated by the max distance scaled by the percent value. The percent value slider allows for better interactivity.

The max distance is the value to move when the slider is set to 100%.

The percent value ranges from -100 to 100 and starts at 0. A value of zero is the starting shape of the selected mesh. You can drag the slider left to decrease the value, or drag it right to increase the value. You can also change the value by typing a new value into the text box next to the slider.

Press Ok to keep your changes or press Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_offsetwin.page000066400000000000000000000001031466047437300176660ustar00rootroot00000000000000PAGE_TITLE=Offset by Normal Window PAGE_CONTENT=Paint Texture Window

The Paint Texture Window is used to create an image file with faces from the model projected onto it. You can then use a paint program of your choice to draw in the contents of your texture. This window uses the texture coordinates that are already assigned to the faces using the Texture Coordinates Window or the Texture Projection Window.

To use the Paint Texture Window select the faces to be painted onto a texture and then select Paint Texture... from the Materials menu.

The Polygons option allows you to define how the polygons should be drawn (filled or empty, with or without lines).

The Vertices option allows to turn drawing of the vertices on and off.

The Save Size options allow you to specify the width and height of the texture to save.

When you are done changing the texture options, select Save Texture... to select a file name to save as, or select Close to close the window without saving a texture image file.

For more details on working with texture maps, see the Materials Window , the Groups Window, the Texture Coordinates Window, and the Texture Projection Window.

mm3d-1.3.15/doc/html/olh_painttexturewin.page000066400000000000000000000001061466047437300211370ustar00rootroot00000000000000PAGE_TITLE=Paint Texture Window PAGE_CONTENT=Plugins Window

The Plugins Window shows you what plugins are installed and active.

Plugins can add support for additional features, see Development § Plugins for details.

Plugins are located in a directory on your hard drive. Plugins can be in subdirectories or symbolic links.

  • Using the Flatpak on Linux, Maverick Model 3D uses $HOME/.var/app/moe.clover.mm3d/data/mm3d/plugins/1.3.
  • On macOS, Maverick Model 3D uses $HOME/Library/Application Support/Maverick Model 3D/plugins/1.3.
  • On other Unix-like systems, Maverick Model 3D uses $HOME/.mm3d/plugins/1.3 and /usr/local/share/mm3d/plugins/1.3, although the shared path can be changed at compile time.
  • On Windows, Maverick Model 3D uses %APPDATA%\Maverick Model 3D\plugins\1.3 and the plugins\1.3 subdirectory where Maverick Model 3D was installed.

The main component of the plugins window is a table that contains a list of plugins. Each plugin should also provide a Version string and a Description string. There is also a Status column that tells you if the plugin is properly initialized and currently active. Filename specifies the full path of the plugin.

All plugins can be disabled at run time by specifying --no-plugins on the command line. Specific plugins can be disabled by specifying --no-plugin=plugin_name on the command line. --no-plugin can be specified more than once.

Press Ok to close the window.

mm3d-1.3.15/doc/html/olh_pluginwin.page000066400000000000000000000000721466047437300177030ustar00rootroot00000000000000PAGE_TITLE=Plugins Window PAGE_CONTENT=Point Details

Overview

A point is is an object that has a name, position, and rotation. It may be attached to up to four bone joint influences.

Points are not true geometric objects. They are simply place-holders in 3D space. A programmer may use a point to define a bolt point. The Quake MD3 filter uses points as "tags" for combining multiple model files into a single final model.

Creating a Point

Use the Create Point tool to create a pone joint.

Other Point Details

You can change the name of a point using the Name Properties panel or Points Window. You can assign bone joint influences to a point using the Influence Properties panel.

See Also

mm3d-1.3.15/doc/html/olh_pointdetails.page000066400000000000000000000000741466047437300203700ustar00rootroot00000000000000PAGE_TITLE=Point Details PAGE_CONTENT=Points Window

The Points Window can be opened by selecting Points... from the Geometry menu. The Points Window allows you to rename points and assign points to a bone joint. To create points, see the Create Point tool.

Points List

The points combo box lists all the points in the model. Selecting a point in the combo box will cause that point to be selected (all other points will be unselected). After selecting a point you can rename it, delete it, or assign it to a bone joint.

Rename

The Rename button allows you to rename a point. Point names do not have to be unique.

Delete

The Delete button deletes a point.

Bone Joint

The Bone Joint drop-down box lists all bone joints in the model. Select a bone joint to assign the point to the specified joint. Note that using the Influences Propertes Panel is more flexible than the Points Window when assigning bone joints.

Press Ok to keep your changes or press Cancel to ignore any changes.

See Also

mm3d-1.3.15/doc/html/olh_pointwin.page000066400000000000000000000000701466047437300175340ustar00rootroot00000000000000PAGE_TITLE=Points Window PAGE_CONTENT=Texture Projection Details

Overview

A Texture Projection helps you apply a 2D image texture map to a group of faces in a spherical or cylindrical fashion. There is a also a plane projection you can use if the Group Map scheme in the coordinate window is not aligned to the axis of your mesh.

A texture projection does not have a 2D texture associated with it. Any faces that use the texture projection to assign texture coordinates will use whatever texture is applied to their group.

Creating Texture Projections

Use the Create Projection tool to create a texture projection. After creating the projection, you can add faces to the the projection by selecting the faces and then selecting the "Texture Projection" from the Projection Properties panel, or by using the "Add Faces To Projection" button in the Projection Window.

Other Texture Projection Details

After creating the projection you can change the type of projection (sphere, cylinder, plane) Using the Projection Window or by selecting the Projection Type from the Projection Properties panel.

You can resize the projection by selecting it and then using the Scale Tool. You can rotate the projection using the Rotate Tool.

On the cylinder and sphere projections you will notice that one of the dashed lines is thicker than the others. This is the "seam", or the location where the texture coordinates wrap around the edges of the 2D texture. You can rotate the projection to change where the seam is applied to the mesh.

See Also

mm3d-1.3.15/doc/html/olh_projectiondetails.page000066400000000000000000000001161466047437300214100ustar00rootroot00000000000000PAGE_TITLE=Texture Projection Details PAGE_CONTENT=Texture Projection Window

The Texture Projection Window is used to adjust how a texture is applied to the faces of the model in a sphere or cylinder mapping. This window is used after you have created a texture projection with the Projection tool. The Texture Projection Window

To change the texture projection properties, select the projection you wish to modify and select Edit Projection in the Main Window's Materials menu.

The texture projection window allows you to change the projection type, rename the projection, adjust the UV range that the texture coordinates are mapped to, adjust where the seam (wrap point) is on the sphere or cylinder, and add or remove faces from the projection. It does not allow you to change individual vertex texture coordinates. If you want to move each vertex individually, use the Texture Coordinates Window.

The Texture Coordinates Window contains a drop-down box at the top of the window with the current projection name, a Texture Frame that shows how the faces are projected onto the texture, and various buttons to change the projection's properties.

The Texture Frame shows the texture used by the selected faces. Faces and vertices are drawn over the texture to show where each face's vertices are applied to the texture. There is also a white box that shows the UV range where the faces are mapped onto the texture.

You can modify the UV Range with the left mouse button. Click and drag on the edges or corners of the UV Range box to move resize the range. Click and drag inside the UV Range box to move the UV Range to a new location on the texture.

You can move the seam (the location where the texture wraps on the cylinder or sphere) by using the right mouse button. Click inside the UV Range box and drag to move the seam left or right. You will see faces wrap from one side of the UV Range box to the other.

You can pan around the Texture Frame by using the arrow buttons inside the frame window, or by middle-click dragging. You can zoom in or out by using the Zoom Buttons or by using the mouse scroll wheel.

Use the Type drop-down box to change the projection types.

Use the Rename button to rename the selected projection.

Use the Zoom Edit Box or Zoom Buttons (magnifying glasses) to zoom the texture view in or out.

The Apply Projection button remaps the faces on this projection to the projection location. Normally this is automatic when the projection is modified. You may need to use this button if you move the texture coordinates manually and want to restore them to the projection location without modifying the projection itself.

The Reset UV Range button sets the minimum and maximum UV coordinates to the edges of the texture (0.0 to 1.0).

The Add Faces to Projection button adds the selected faces in the main window to the projection in the Texture Projection Window.

The Remove Faces button removes the selected faces in the main window from the projection.

Press Close to close the Texture Projection Window.

See Also

mm3d-1.3.15/doc/html/olh_projectionwin.page000066400000000000000000000001111466047437300205530ustar00rootroot00000000000000PAGE_TITLE=Texture Projection Window PAGE_CONTENT=Properties Panel

Overview

The Properties Panel is a dockable window. It contains context-sensitive model data so that you can edit some model properties directly. For example, if you have the faces of a mesh selected, it will allow you to directly edit the center position as well as assign groups and materials.

To open the Properties Panel, select "View | Show Properties". To hide the Properties Panel, select "View | Hide Properties" or click the "X" in the upper-right corner of the panel.

The Properties Panel has several sub-panels that appear depending on what you have selected. Below is a table that lists which panels appear when various items are selected.

Selection Panel
Point or Bone Joint
(exactly one selected)
Name
Any Geometry Position (center)
Point or Bone Joint
(exactly one selected)
Rotation
Faces (triangles) Group
Texture Projections Projection
Vertices or Points
(if bone joints exist)
Influences

Name

The Name Panel has a text box with the name of the selected point or bone joint. You can change this text to change the name of the selection. This panel only appears if you have exactly one point or bone joint selected.

Position

The Position Panel has text boxes that list the center coordinates for the selected geometry. You can edit the position directly by changing these numbers.

Rotation

The Rotation Panel allows you to change the rotation of a point or a bone joint. The point rotation applies to non-animated points or points in a frame animation. The joint rotation only applies during skeletal animations (it sets a rotation keyframe).

Group

The Group Panel appears when you have one or more faces (triangles) selected. With this panel you can assign a group to the selected faces. You can select the material for the group (note that material assignments apply to the entire group; so this changes the material for all faces in the group, not just the selected faces). You can also select a Texture Projection for the selected faces if you want to apply a sphere or cylinder texture map.

Projection

The Projection Panel has a drop-down box that allows you to change the projection type. See Texture Projection Details for more information.

Influences

The Influences Panel appears when there are bone joints in the model and you have some geometry selected. With the Influences panel you can assign up to four bone joints to influence the movement of the selected geometry. See Bone Joint Details for more information.

mm3d-1.3.15/doc/html/olh_properties.page000066400000000000000000000000751466047437300200660ustar00rootroot00000000000000PAGE_TITLE=Properties Panel PAGE_CONTENT=Contents

Loading Skins and Textures

MD2 models should be about 44 GL units tall and centered on the origin.

MD2 skins are a bit tricky to load. In general the MD2 filter attempts to load skins in the following manner:

  • Load any skin image file specified in the model, assuming it is in the same directory as the model
  • If no skins are in the model, or no skins listed in the model could be loaded, the filter will try to load any image that begins with the characters in the model's filename (excepting the extension). So model.md2 would load any model*.* file in the same directory that the program has image filters for.
  • Quake uses PCX files for skins. Maverick Model 3D will allow you to use any image format, but you should use PCX if you want others to be able to load your model.

When saving models as MD2 it is important to note that MD2 models only use one texture as a skin at any given time. All vertices of the model must be mapped to the same texture. You may include different colorings of the same texture for different skins and add those to the model as additional textures. Those textures should be available as alternate skins for the model.

Animations

MD2 animations specify vertex positions for each vertex for each frame of each animation. This can be tedious. When creating a model from scratch it is usually easier to save the model in MM3D format and create skeletal animations. Then when you are ready to export to MD2, convert the skeletal animations to frame animations using the Animation Sets Window and save as an MD2.

Because of the enormous overhead of saving undo information for animations upon insertion and deletion of vertex and face primitives, you are not allowed to add or remove primitives from a model that has frame animations. You may, however, merge another existing model into the one you are working on. This is another reason why it is best to work with MM3D files and skeletal animations and only export MD2 files when necessary.

Triangle Strips

MD2 files save triangle strip information for optimized model drawing. Maverick Model 3D is rather dumb about triangle strips by default and creates a triangle fan for each triangle. If you want smarter triangle strips see the ACTC triangle stripping plugin on the Misfit Model 3D plugins webpage.

Note that plugins are only available on Linux and Mac OS X systems. Plugin support is not available on Windows systems at this time.

mm3d-1.3.15/doc/html/olh_quakemd2.page000066400000000000000000000000721466047437300174000ustar00rootroot00000000000000PAGE_TITLE=Quake MD2 Notes PAGE_CONTENT=Contents

Model Types

An MD3 model contains a set of meshes and tags. Some in-game Quake models are actually composites of several MD3 files. For example, a player model consists of head.md3, upper.md3, and lower.md3 files. Tags within these files tell Quake how to assemble the model files into one single model.

Maverick Model 3D is capable of loading and saving a single MD3 file, or loading and saving all files in a player model as one model.

If you attempt to load a file named head.md3, upper.md3, or lower.md3 and the other two files are present, MM3D will ask you if you want to load all sections as a player model. Answer Yes to load all three files as one model. Answer No to load only the specified model file.

When you save a model, MM3D will ask to save as a player model if the following conditions are met. If any of the following conditions are not met, the model will be saved as a single MD3 file.

  • The saving file name is head.md3, upper.md3, or lower.md3
  • The model contains at least two points; named tag_torso and tag_head.
  • The model contains at least one group beginning with h_ (for head meshes), u_ (for upper body meshes), and l_ (for lower body meshes).
  • The MD3_composite metadata value is not set to 0 (zero)

Skin files are read automatically for player and non-player models. Skin files are never created or modified by MM3D. If you wish to change or create a skin file you must do this step manually.

Player Models

A typical MD3 player model might be 60 GL units tall and 30 wide. The base of the feet should be at -24.

Player models consist of three model files: head.md3, upper.md3, and lower.md3. Usually these correspond to head, torso, and legs respectively. These three models are connected with tags (Points in MM3D). The weapon and Team Arena CTF flag the player is holding are also connected to the player model with a tag.

Typical tags for player models:
Model File Tags in model
head.md3 tag_head
upper.md3 tag_head, tag_torso, tag_weapon, (Team Arena) tag_flag
lower.md3 tag_torso

Other unknown tags will be saved in all model files which can be used by Quake 3 mods.

You can have multiple skins for a player model. Each md3 file should have a corresponding skin file (head_default.skin, lower_default.skin, upper_default.skin). Usually there are at least three skins for a player model: _default.skin, _red.skin, and _blue.skin. For more on skins see Skins and Textures.

Player models need an animation.cfg file to specify which frames correspond to in-game actions. An animation.cfg file might look like the example below. All animation sequences should have at least one frame.

Head models do not have animations in Quake 3. The head is attached to the tag_head tag in the upper body model. If you want to animate the head, you can hide the tag_head inside the model and do all the head animations in the torso (upper) meshes. You should still make a head model because it is displayed on the HUD.

It's possible to add support for head animations by modifying the Quake 3 code. MM3D supports ALL_ animation prefix for head/upper/lower animations and HEAD_ animation prefix for head-only animations.

The animation name affects which sections of the player the animation is exported to.
Animation Prefix Models
ALL_ upper.md3, lower.md3, head.md3
BOTH_ upper.md3, lower.md3
TORSO_ upper.md3
LEGS_ lower.md3
HEAD_ head.md3

Quake 3 player model animations must be in the same order as listed below. The optional Quake III: Team Arena animations at the end of the list require tag_flag tag in the upper model to be used in-game. It is recommended to set Loop for each animation as listed below in the Animation Panel. This controls whether the individual animations are marked as looping in the exported animation.cfg.
Animation Loop Description
BOTH_DEATH1 no Random death 1, for example twirl death.
BOTH_DEAD1 no Set animation frame count to 0 in MM3D to use last frame of BOTH_DEATH1.
BOTH_DEATH2 no Random death 2, for example other side twirl death.
BOTH_DEAD2 no Set animation frame count to 0 in MM3D to use last frame of BOTH_DEATH2.
BOTH_DEATH3 no Random death 3, for example backflip death.
BOTH_DEAD3 no Set animation frame count to 0 in MM3D to use last frame of BOTH_DEATH3.
TORSO_GESTURE no Taunt.
TORSO_ATTACK no Firing gun. This is synced with first person hand model and must have 6 frames.
TORSO_ATTACK2 no Guntlet punch. This is synced with first person hand model and must have 6 frames.
TORSO_DROP no Drop weapon. This is synced with first person hand model and must have 5 frames.
TORSO_RAISE no Raise weapon. This is synced with first person hand model and must have 4 frames.
TORSO_STAND no Holding gun.
TORSO_STAND2 no Holding gauntlet.
LEGS_WALKCR yes Walking while crouching. It is played in reverse when walking backward.
LEGS_WALK yes Walking forward. It is played in reverse when walking backward.
LEGS_RUN yes Running forward.
LEGS_BACK yes Running backward.
LEGS_SWIM yes Swimming.
LEGS_JUMP no Jump forward (jump off the ground).
LEGS_LAND no Jump forward (land).
LEGS_JUMPB no Jump backward (jump off the ground).
LEGS_LANDB no Jump backward (land).
LEGS_IDLE yes Idle standing.
LEGS_IDLECR yes Idle crouching.
LEGS_TURN yes Standing still and turning left or right.
TORSO_GETFLAG no Team Arena; give order to get flag.
TORSO_GUARDBASE no Team Arena; give order to guard base.
TORSO_PATROL no Team Arena; give order to patrol.
TORSO_FOLLOWME no Team Arena; give order to follow me.
TORSO_AFFIRMATIVE no Team Arena; confirm order.
TORSO_NEGATIVE no Team Arena; reject order.

Example animation.cfg

//Quake3 player animation file

sex m
footsteps normal

// first frame, num frames, looping frames, frames per second

0       32      0       25              // BOTH_DEATH1
32      1       0       25              // BOTH_DEAD1
32      35      0       20              // BOTH_DEATH2
67      1       0       20              // BOTH_DEAD2
68      25      0       25              // BOTH_DEATH3
93      1       0       25              // BOTH_DEAD3

94      39      0       15              // TORSO_GESTURE
133     7       0       15              // TORSO_ATTACK  (MUST NOT CHANGE -- hand animation is synced to this)
140     11      0       15              // TORSO_ATTACK2 (MUST NOT CHANGE -- hand animation is synced to this)
151     5       0       15              // TORSO_DROP    (MUST NOT CHANGE -- hand animation is synced to this)
156     6       0       15              // TORSO_RAISE   (MUST NOT CHANGE -- hand animation is synced to this)
162     31      0       10              // TORSO_STAND
193     11      0       10              // TORSO_STAND2

94      12      12      15              // LEGS_WALKCR
107     12      12      15              // LEGS_WALK
120     12      12      17              // LEGS_RUN
133     14      12      17              // LEGS_BACK
148     12      12      14              // LEGS_SWIM
159     6       0       8               // LEGS_JUMP
167     6       0       12              // LEGS_LAND
172     6       0       10              // LEGS_JUMPB
178     6       0       10              // LEGS_LANDB
185     6       6       5               // LEGS_IDLE
192     7       7       5               // LEGS_IDLECR
200     6       6       20              // LEGS_TURN

Player Metadata

Set MD3_CFG_* metadata to insert a keyword into the animation.cfg. For example, set MD3_CFG_sex meta data to value f to specify that the player model is female. Keywords and values are automatically detected when loading a player model. Any arbitraty keywords can be used. Keywords should not start with an animation prefix (ALL_, BOTH_, TORSO_, LEGS_, or HEAD_) as it may be detected as an animation when loaded by MM3D. Quake 3 only supports sex, footsteps, headoffset, fixedtorso, and fixedlegs. Example:

sex f // m, f, or n
footsteps normal // footstep sfx: normal, boot, flesh, mech, or energy
headoffset 0 0 0 // head offset when displayed on HUD
fixedtorso // don't rotate torso pitch when looking up or down
fixedlegs // don't rotate legs (always align with torso)

Set the MD3_AnimKeyword metadata value to 1 to list animation names at the beginning of line in animation.cfg. This is automatically detected when loading a player model. It is not supported by Quake 3. It is used by Elite Force Single Player and Turtle Arena. Example:

BOTH_DEATH1 0       32      0       25
BOTH_DEAD1  32      1       0       25

Set the MD3_NoSyncWarning metadata value to 1 to disabling writing sync warnings in animation.cfg for TORSO_ATTACK, TORSO_ATTACK2, TORSO_DROP, and TORSO_RAISE. It is used by Turtle Arena which does not feature first person hand models.

Set the MD3_EliteLoop metadata value to 1 to use Elite Force Single Player style looping values in animation.cfg. -1 for not looping and 0 for looping instead of Quake 3 style 0 for not looping and number of frames for looping. It is not supported by Quake 3. This is automatically detected when loading a player model if a looping value is -1.

Weapon Models

A typical MD3 weapon might be 5 GL units wide and 45 long. They usually would have no animations, although it should be possible to add animation by modifying the Quake 3 code. You can also add texture animations with shaders. Weapons are attached to the tag_weapon tag in the player model's upper model or first person hand model. Weapons typically have tag named tag_weapon at the model origin (0,0,0), however it is not used by the Quake 3 code. Some weapons have a tag named tag_flash where Quake 3 attaches the muzzle flash model (typically (weapon)_flash.md3 in the same directory). Some weapons have a tag named tag_barrel where Quake 3 attaches the barrel model (typically (weapon)_barrel.md3 in the same directory).

Hand Models

A hand model is used for positioning the first person weapon model. The model consists of only an animated tag_weapon tag and is not rendered by Quake 3.

Tags

Quake 3 uses tags to join models to each other. MM3D loads tags as points, so if you want to create a tag make a Point (more details about points). The point name should be whatever you want the name of the tag to be. The red axis of the tag is forward, the blue axis of the tag is up, and the green axis of the tag is left. For example you want the red side of that tag to face front for tag_weapon.

The MD3 orientation for tags is not the default orientation for MM3D points, so you must rotate any points you create into the correct orientation.

Animations

MD3 animations specify vertex positions for each vertex for each frame of each animation. This can be tedious. When creating a model from scratch it is usually easier to save the model in MM3D format and create skeletal animations. Then when you are ready to export to MD3, convert the skeletal animations to frame animations using the Animation Sets Window and save as an MD3.

Because of the enormous overhead of saving undo information for animations upon insertion and deletion of vertex and face primitives, you are not allowed to add or remove primitives from a model that has frame animations. You may, however, merge another existing model into the one you are working on. This is another reason why it is best to work with MM3D files and skeletal animations and only export MD3 files when necessary.

MM3D will save all frame (vertex, mesh deformation) animations when saving as MD3.

Paths

When an MD3 model is saved, path information is stored in the model. Quake3 uses a VFS (virtual file system), and you must tell Quake3 where your files are located in this VFS. For example a player model might be located in the Quake3 VFS at models/players/man/. So all the models and textures should probably be located in there and when you make a pk3 file (zip file really), it should contain that directory hierarchy.

We still need MM3D to save paths and filenames with models/players/man/. This is where the MD3_PATH metadata attribute comes in. You should set the MD3_PATH metadata attribute to models/players/man/. MD3 Filter will append all filenames that gets stored in the MD3 file to MD3_PATH. So your body.tga texture when saved would be written in the MD3 file as models/players/man/body.tga. When quake3 opens it that is where it will look in the VFS.

Note: The Quake 3 VFS does not start with a /.

Skins and Textures

The MD3 filter can load any image type as a texture if MM3D can use it. When looking for a texture file it ignores the extension and finds any loadable texture with the the right file name (regardless of extension). For example if the model says it should use a file called body.tga, MD3 Filter could use body.png if it exists.

MD3 filter looks for texture images and skin files in the same directory as the model regardless of the values for paths contained in the model.

In general the MD3 filter attempts to load skins in the following manner:

  • First it will look for .skin files with the following pattern. {modelName}_*.skin It will load all images inside those skin files. It will set the material to the image contained in {modelName}_default.skin.
  • If no .skins files are there it will use shaderName specified in the model to look for a texture file.
  • If that does not exist it will look for a valid texture whose filename matches {meshName}.*

When saving MD3 models it sets the shader name to the name of the texture that is assigned to the group. Also note that Quake3 by default only loads TGA and JPG files.

A skin file should be of the form indicated below.

mesh_name,MD3_PATH/textureFilename

and example upper_default.skin:

u_body,models/players/man/default_b.tga
u_cape,models/players/man/default_c.tga

Shaders

Currently MD3 filter will not load Quake3 .shader files. These are files that could do complex OpenGL operations. You can still use shader files with your model inside Quake3 by creating the shader manually. Here is a example:

Create a shader file in the scripts directory (at the top of the VFS next to models directory) called player-(your model).shader containing:

// comment: Use MD3_PATH/textureFilename without extension.
models/players/man/default_b
{
	{
		map models/players/man/default_b.tga // with extension
		rgbGen wave sin 0.5 0.5 0 1.5 // make color be a sine wave
	}
}

models/players/man/default_c
{
	{
		map models/players/man/default_c.tga
		rgbGen identityLighting // full bright
	}
}

Quake3 should see the shader and use it instead of just the texture.

MD3 Limits

The MD3 file format has several limits that you should consider when making a model for this format. These are outlined below. If your model has more that the MD3 limit, the file will not be saved correctly and you should get a error message.

  • Only faces and verticies in a group get saved.
  • The MD3_PATH+file name cannot be longer than 63 characters.  (Textures and model names)
  • The group name and point name cannot be longer than 63 characters.

Quake3 Limits

Quake3 only supports a limit amount of geometry. These are outlined below. Other games may have different limits. You should get a error message in the Quake3 console if your model has more geometry than the Quake3 limit.

  • Maximum of 1999 Triangles per Group
  • Maximum of 999 Verticies per Group

Vertexes in MM3D are shared between materials and texture coordinates. Exporting to MD3 may have a greater number of vertexes. You cannot solely go off of the vertex count displayed in the editor.

.skin files in Quake3 have a limited number of groups. More than that will cause Quake3 to write memory out of bounds and may cause issues.

  • Maximum of 32 Groups
mm3d-1.3.15/doc/html/olh_quakemd3.page000066400000000000000000000000721466047437300174010ustar00rootroot00000000000000PAGE_TITLE=Quake MD3 Notes PAGE_CONTENT=MD3 Export Options

The MD3 Export Options window is displayed when you save a model in Quake MD3 format. See Quake MD3 for more details about player models and animation.cfg.

The Save as a Quake 3 Player checkbox allows you indicate if you want to export as a Quake 3 composite player model (head.md3, upper.md3, and lower.md3 models). If it is grayed out, see the list of requirements for export on the MD3 page.

The Save Quake 3 animation.cfg checkbox allows you to indicate if you want to write "animation.cfg" file along side the model. This file contains a list of animations with looping and frames per-second information and can contain other arbitry data from MD3_CFG_* meta data.

Click Ok to save the model with the specified options or click Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_quakemd3prompt.page000066400000000000000000000001031466047437300206360ustar00rootroot00000000000000PAGE_TITLE=MD3 Export Options PAGE_CONTENT=SMD Export Options

The SMD Export Options window is displayed when you save a model in Studiomdl Data (SMD) format. It allows you to specify things like whether or not to save the meshes in the file and which animation to save.

Typically there is a SMD model that contains meshes and the bind pose skeleton and separate SMD models for each animation without meshes.

The Model Type options specify whether to save Reference (meshes with skeleton bind pose) or a skeleton Animation.

The Save Points as Bone Joints checkbox allows you to indicate if you want the points to be saved as skeleton bone joints or not at all. Points can only have one bone joint influence.

The Vertex Format options specify which SMD vertex format you want to save as. If no vertices in your model are assigned to more than one bone joint, select GoldSrc. Otherwise, select Source. This only affects Mesh SMD files.

  • GoldSrc - Original file format, a vertex can only be assigned to one bone joint.
  • Source - A vertex may be assigned to multiple bone joints.

The animation list box allows you to specify which animation you want to be saved in the file. This only affects Animation SMD files.

Click Ok to save the model with the specified options or click Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_smdprompt.page000066400000000000000000000000761466047437300177200ustar00rootroot00000000000000PAGE_TITLE=SMD Export Options PAGE_CONTENT=Spherify Window

The Spherify Window is used to interactively morph the selected portion of a model into a spherical shape. It is made up of a slider and a text entry box for the spherify value.

The center of the spherify command is the center of the selected mesh. The sphere radius is the distance from the center of the mesh to the farthest selected vertex. As the spherify value increases, selected vertices are pushed away from the center until they are at the same distance from the center as the farthest vertex. A negative spherify value pulls internal selected vertices closer to the center of the selected mesh.

The spherify value ranges from -100 to 100 and starts at 0. A value of zero is the starting shape of the selected mesh. A higher value means the shape is more spherical, a lower value means the shape is less spherical. You can drag the slider left to decrease the value, or drag it right to increase the value. You can also change the value by typing a new value into the text box next to the slider.

Press Ok to keep your changes or press Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_spherifywin.page000066400000000000000000000000751466047437300202410ustar00rootroot00000000000000PAGE_TITLE=Spherify Window PAGE_CONTENT=Texture Coordinate Details

Overview

Texture Coordinates define how a 2D image texture map is applied to the faces of a group. Each vertex of each triangle has its own set of texture coordinates. If two triangles share a vertex, each triangle may have different texture coordinates for the same vertex.

Creating Texture Coordinates

Texture coordinates are created implicitly when a triangle is created. The default coordinates are not very sensible or useful.

Other Texture Coordinate Details

To change the way the texture is mapped onto a group of faces you will need to use the Texture Coordinates Window. Typically when using the Texture Coordinate Window you will start by selecting the "Group" map scheme. This method of setting up starting texture coordinates can be somewhat limiting. Sometimes using a Texture Projection gives you a better starting point for texture coordinate assignment.

After using a group map scheme or texture projection to get the texture coordinates in roughly the place you want them, you can use the viewport in the Texture Coordinates Window to select triangle vertices and move the coordinates. The viewport in the texture coordinates window follows most of the same conventions as the model viewports.

See Also

mm3d-1.3.15/doc/html/olh_texturecoorddetails.page000066400000000000000000000001201466047437300217560ustar00rootroot00000000000000PAGE_TITLE=Texture Coordinate Details PAGE_CONTENT=Texture Coordinates Window

The Texture Coordinates Window is used to change how a texture is applied to the faces of the model. It applies each vertex of a face to a point on a texture. The texture is then stretched between each vertex on the face to create the final texture appearance on the model. To change the texture coordinates, select faces you wish to modify and select Edit Texture Coordinates in the Main Window's Groups menu.

The Texture Coordinates Window contains four major elements:

  • Texture Frame - Shows the texture image with vertices and faces drawn over it.
  • Mouse Tool Buttons - Determine what happens when you use the mouse on the Texture Frame.
  • Scale Options Frame - Gives some control over how the scaling operation works.
  • Map Scheme Buttons - Change the initial coordinate mapping scheme for the selected faces.

The Texture Frame viewport shows the texture used by the selected faces. Faces and vertices are drawn over the texture to show where each face's vertices are applied to the texture. Vertices can be selected, unselected and moved using the left and right mouse buttons. The exact operation performed by the mouse depends on which Mouse Tool is selected.

The Texture Frame is very similar to the model viewports. You can pan around the Texture Frame by using the arrow buttons inside the frame window, or by middle-click dragging. You can zoom in or out by using the Zoom Buttons or by using the mouse scroll wheel.

The Rotate CCW and Rotate CW buttons rotate the selected texture coordinate vertices 90 degrees counter-clockwise and clockwise.

The V Flip and H Flip buttons will perform a vertical or horizontal flip on the selected texture coordinate vertices.

The Mouse Tool buttons determine what the mouse buttons do when used in the Texture Frame. Most tools operate in a similar fashion as the tools in the view window (an exception is that you may only select vertices).

  • The Select button allows you to select vertices with the left mouse button and unselect vertices with the right mouse button. You can hold the shift key to make your selection cumulative.
  • The Move button allows you to move selected vertices. You can hold the shift key to only move in one dimension.
  • The Rotate button allows you to rotate selected vertices. Right click sets the rotation point. You can hold the shift key to rotate selected vertices in 15 degree increments.
  • The Scale button allows you to scale the distance between selected vertices. You can hold the shift key to only scale in one dimension. Using the Scale Options frame you can control whether you scale from the center of the selected vertices or the far corner and whether or not the 2D aspect ratio of the selection is preserved during the scaling.

The Map Scheme buttons are used to set the initial coordinates for the faces. The Triangle mapping uses the same coordinates for all three vertices of each face. The Quad mapping uses three coordinates from two triangles for alternating faces. The Group mapping projects the selected faces from a specified direction onto the texture image. Each vertex has its own coordinate and may be moved independently.

When you click on the Group radio button you will be prompted to chose one of six directions. Maverick Model 3D will take the selected faces as they appear in the orthographic view of your chosing, scale them to the size of the texture image, and project them onto the texture image.

Texture Projections are a more powerful alternative to the group mapping scheme.

The Reset Coordinates button is used to reapply the current map scheme to the texture coordinates. If you click this button while using the Group mapping scheme you will be asked to pick a direction again.

The next time you open the Texture Coordinates Window with the same faces selected, it will show the vertex coordinates that you set previously.

Press Close to close the Window.

See Also

mm3d-1.3.15/doc/html/olh_texturecoordwin.page000066400000000000000000000001141466047437300211310ustar00rootroot00000000000000PAGE_TITLE=Texture Coordinates Window PAGE_CONTENT=Materials Window

The Materials Window is used to add or remove model materials (aka, textures) You may also modify the reflective properties of the material. Select Edit Materials from the Main Window's Matererials menu.

The combo box at the top of the window contains the names of all materials associated with this model. You can select a material name and use the lighting property sliders to adjust the reflective properties (Ambient, Diffuse, Specular, Emissive, and Shininess) of the material.

  • Ambient - Light reflected from no particular direction.
  • Diffuse - Light reflected from the primary light source.
  • Specular - Light reflected which brings out highlights in the material.
  • Emmisive - Light emmited from the material's surface.

Each lighting color value ranges from -1.0 to 1.0. You can drag the slider left to decrease a color's value, or drag it right to increase a color's value. You can also change a value by typing a new value into the text box next to the slider.

The material preview can be a flat texture image or a 3D preview of the texture mapped onto a cube. Use the material preview combo box to switch between preview modes.

To add a new material click the New Material and enter a name. To add a 2D texture image to the material, click the Set Texture button and select a file. The file dialog box will show you which image file formats are supported. You should select an image with dimensions that are a power of 2 (ie, 32x32, 128x256, etc...). Other sizes should work with Maverick Model 3D, but may not work with other rendering engines.

To remove a texture from the current material click the X button next to Set Texture.

To remove a material from the model, select the material name in the combo box and click the Delete button (this only removes the material from the model, the actual file on disk is not modified in any way).

To rename a material click the Rename button. This may be useful if you want the material name to match a group name, or if you have two materials with the same image but different lighting values. Names do not need to be unique (though duplicate names may be confusing for other people using the model).

You can change the texture wrapping behavior by changing the value of the the Wrap/Clamp combo boxes. Wrap will cause the texture to wrap for texture coordinates that are outside of the 0.0 to 1.0 range. Clamp will cause the renderer to extend the edge of the texture in the desired direction. The X and Y clamp values can be changed independently.

Press Ok to keep your changes or press Cancel to ignore any changes.

See Also

mm3d-1.3.15/doc/html/olh_texturewin.page000066400000000000000000000000751466047437300201100ustar00rootroot00000000000000PAGE_TITLE=Materials Window PAGE_CONTENT= Quick Tips

This page describes some short tips that can help you use Maverick Model 3D more effectively.

General Modeling Tips

  • Properties Panel -- Some parts of a 3D model will have modifiable properties that can be changed through dialog boxes or the Properties Panel. The Properties Panel is a context-sensitive dockable window that displays modifiable properties for the selected portion of the 3D model. Using the Properties Panel may be more convenient than using tools, commands, or dialog boxes to accomplish some tasks. Select "View | Show Properties" to use the Properties Panel.
  • Background Images -- When creating a model, it is sometimes helpful to have a 2D image for reference to help with getting the shape or scale of some object correct. You can project a 2D image into any viewport using Model | Select Background Image.
  • Snap to Grid/Vertex -- Use "Tools | Snap To | Grid" and "Tools | Snap To | Vertex" to help align the current tool with the grid lines and existing vertices.
  • Drag Vertex -- Use Drag vertex on edge to move a vertex along one of the triangle edges it is connected to. The tool will attempt to follow the edge that is closest to the mouse.
  • Edge Turn -- If two triangle form a concave angle when you want a convex angle (or vice-versa), use Edge turn. See the link for an example of when this is useful.
  • Extrude -- Use "Extrude" to take a set of faces and extend it out from the object it is connected to. There is a geometry command version and a tool version.
  • Shift Key -- Some tools modify their behavior if you hold the shift key while using them. For example, the move tool will operate only in one dimension and the rotate tool will rotate in 15 degree increments. Often these behaviors are noted in the status bar when you change tools. Any special behavior of the shift key for a tool will be described in the tool documentation
  • Face Out -- Triangles/faces only reflect light off of one side. If the dark side of a triangle is facing out toward the camera, you need to invert the normal of the triangle to reverse the direction it faces. If an enclosed 3D shape has some triangles facing in and some facing out, you can select the entire shape and use Geometry | Normals | Normals face out to correct any faces that are wrong. This can be easier than manually selecting faces and inverting them. Note that this only works if the faces are part of a complete, enclosed 3D shape.
  • Snap Together -- You can use Snap Together to make nearby vertices align themselves. Optionally you can have them weld into a single vertex. This operation can work on all selected vertcies, or pairs of nearby vertices.

Viewports

  • Swith to Orthographic -- You may only use selection and modification tools in orthographic projections. If you see something in a 3D perspective projection that looks wrong and you are not sure which faces or vertices to select from an orthographic viewport, you can switch the perspective viewport into an orthographic viewport from the same angle by pressing the the left quote/backtick key (`).
  • Rotate 180 -- The Backslash key (\) will rotate the viewport 180 degrees so that you are looking at the opposite side of the object in your viewport.
  • Pan -- Click and drag the middle mouse button in a viewport to pan. You can also use the arrow keys to pan any viewport that is highlighted with mouse focus.
  • Zoom -- Use the vertical scroll wheel or the plus/minus keys to zoom in or out.
  • Rotate -- Use Ctrl + scroll wheel or Ctrl + plus/minus keys to rotate the viewport around the viewing axis.
  • Save/Restore View Angle -- Pressing Ctrl + <Number Key 1-9> will save the current rotation and zoom level of the highlighted viewport. Pressing the number key alone will recall the saved position and rotation. Pressing 0 will center the viewport on the origin.
  • Frame Selected/All -- Use "View | Frame Selected" or "View | Frame All" to and zoom your viewport on the object of interest (note that this changes all viewports).

Texturing

  • Texture Groups -- To apply textures to geometry, the faces/triangles must be part of a group. The texture is then applied to the group.
  • Texture Projections -- Texture unwrapping is not supported, but using cylinder and sphere texture projections in addition to paint texture may give you a good starting point for applying textures.
  • Reload Textures -- If you are modifying your textures in a paint program, you can reload the model textures without restarting by selecting the "Reload Textures" option in the Materials menu.

Animations

  • Prefer Skeletal Animations -- Skeletal animations tend to be easier to work with than frame animations. For model formats that require frame (mesh deformation) animations--such as the quake formats--it is best to create skeletal animations and then convert those animations to frame animations before exporting to the desired format.
  • Setting Keyframes -- Keyframes are automatically created when you move or rotate a joint. If you want to create a keyframe without rotating or moving, use the "Set Rotation/Translation Keyframe" options.
  • Prefer Rotation Keyframes -- Beginners sometimes try to move the joints instead of rotating. Use rotation on the bone joints to animate your model instead of translation (think about how you move your own arms and legs).
  • Set starting and ending keyframes -- It is a good idea to set a keyframe in the first and last frame of an animation for every joint that will be animated during the animation. This will give you more predictable results regardless of whether the animation is looping or not.

Alpha Blending

  • Backfacing Polygons -- When working with transparent textures on a model, keep in mind that back-facing polygons are often not rendered by various rendering engines. Typically you will want to render back-facing polygons when you are modeling. However you can disable back-facing polygons from the View menu if you want to see what your model will look like.
  • Offset Co-planar Triangles -- Since triangles only render facing one direction, transparent triangles are sometimes doubled so that they are rendered properly when viewed from both directions. If these triangles are exactly co-planar you may see rendering artifacts that cause part of the triangle that should be rendered to be obscured. To prevent this, put a slight gap between the co-planar triangles.

Importing and Exporting

  • Work in MM3D Format -- Some supported file formats do not mix well with the way MM3D stores data internally. When working with 3D models on a regular basis it is often a good idea to save in MM3D format and then use "File | Export" to save in your desired format when you have something completed. There are many more exportable formats than save formats.
  • Import Animations -- You can use "Model | Import Animations" to load animations from another model with an identical skeletal structure. The skeleton will be checked to make sure that the parent/child joint relationships are correct. The position and rotation of the joints will not be checked, but if they are not identical the results of the import will probably not be what you expect.
  • Model Merge -- You can use "Model | Merge" to import another model into the current one. There are options for scaling and rotating as well as how to deal with animations. The merge feature can be useful if you have frame animations in your model, since adding and deleting geometry in general is not allowed in that case.
  • Format-Specific Notes -- Some model export formats have special considerations that you must keep in mind when you are working with them. See the links below for details.

Advanced

  • Boolean Operations -- Use Boolean operations to modify one 3D object based on the shape of another 3D object. Boolean operations can combine two shapes into one, subtract the volume of one shape from another, or find the common volume between two shapes. After using a boolean operation you may want to use Simplify Mesh to combine some newly created faces.
  • Custom Keyboard Shortcuts -- It is possible to customize most menu shortcuts. There is no graphical user interface to do this, but you can edit a file called keycfg.in in the $(HOME)/.mm3d subdirectory (on Win32 this is userhome in the Maverick Model 3D Program Files directory). Simply specify the name of the function you want to bind and then the key combination you want to bind it to. See keycfg.out in the same directory for the current bindings. Anything you do not specify in your keycfg.in will use the default setting.
mm3d-1.3.15/doc/html/olh_tips.page000066400000000000000000000000611466047437300166440ustar00rootroot00000000000000PAGE_TITLE=Quick Tips PAGE_CONTENT=Tools

General Usage

Tools


Using the toolbar

The Toolbar has a set of tool buttons. The tool buttons represent tools that allow you to manipulate a model interactively. For example, you can select the "Create Cube" tool and then click and drag the mouse in a viewport on the Main Window to create a cube.

There are two types of tools in the toolbar: Creation tools and manipulation tools. Creation tools create new vertices, faces, and joints interactively in various shapes such as cubes and spheres. Manipulation tools allow you to interactively change faces, vertices, and joints which already exist. Manipulation tools include moving, scaling, and rotation tools. Typically manipulation tools only operate on vertices, faces, or joints which have been selected with the "Select" tool.

It is important to note that faces often share vertices. Unselected faces may be modified by changes to vertices which are also part of a selected face. To disconnect faces from each other so that they do not share vertices, see the Unweld vertices command.

Some tools have additional options, which appear in an a separate toolbar. For example, the sphere tool has an option that specifies how many triangles the sphere will be composed of.


Snap to Grid/Vertex

The tools menu contains a "Snap To" submenu. In this submenu there are two options: "Snap to Grid" and "Snap to Vertex". The former will cause objects dragged by the mouse to align with the nearest grid line when it comes close enough. The latter will cause the objects to align with the nearest vertex.

Tools

Select

Overview

Select vertex tool icon Select face tool icon Select connected tool icon Select group tool icon Select bone joint tool icon Select point tool icon Select projection tool icon
There are seven Select tools that allow you to select vertices, faces, connected meshes, groups, bone joints, points, and texture projections. Each behaves in a similar manner as described below.

Mouse operations

Click and drag in a canvas viewport with the left mouse button. This will create a bouding box. When you release the mouse button all objects of the desired type (vertices, faces, groups, or joints) that are within the bouding box will be selected. In the case of faces or groups, the entire face or group does not need to be within the bouding box. If any portion of the object is within the bouding box it will be selected.

Each time you press the left mouse button you begin a new selection. If you want the selection to be cumulative, you may hold a shift key down to prevent the tool from unselecting vertices. Using the shift key in this way you may select multiple objects by repeatedly using the left mouse button.

The right mouse button is used to unselect objects. You do not need to hold the shift key down to prevent clearing all of the current selection. This is implicit when using the right mouse button.

The select faces tool has an option to select back-facing faces. If checked, any faces in the selected region will be selected (this is the default behavior). If unchecked, only faces that are facing the camera in the selected region will be selected.

Animation mode

The select tools operate the same way in animation mode as they do in model edit mode; however, some animation types do not allow you to select some objects. For example, in skeletal animation mode you can only manipulate bone joints.

Keyboard shortcuts

  • V - Select Vertices
  • F - Select Faces
  • G - Select Groups
  • C - Select Connected
  • B - Select Bone Joints
  • T - Select Points

Move

Overview

Move tool icon
The Move tool moves selected vertices and faces.

Mouse operations

Press and hold the left or right mouse button in a canvas view to begin a move operation. Drag the mouse with a button down to move selected vertices or faces in a canvas view.

You can force the move tool to only move in one dimension by holding the shift key. The first dimension the tool detects movement on will be the only dimension you can move the selected primitives in.

Animation mode

In animation mode the move tool moves vertices or bone joints. The move tool may have no effect on certain types of animations. In skeletal animations, moving a bone joint will set a translation keyframe for the current frame of the current animation. For frame animations, moving vertices will set the vertex position for the current frame of the current animation.

Keyboard shortcuts

  • M - Move Tool

Rotate

Overview

Rotate tool icon
The Rotate tool rotates selected vertices and faces around a point.

Mouse operations

Press and hold the left mouse button in a canvas view to begin a rotate operation. Drag the mouse around the rotation point (default rotation point is the center of the seleced object, but this can be changed with the right mouse button).

Hold the shift button to rotate selected vertices in 15 degree increments. This can be useful if you want to rotate something by an exact amount (like 15, 30, 45, 90, or 180 degrees.

The right mouse button sets the rotation point in the canvas view. The rotation point is indicated by a green crosshair.

Animation mode

In animation mode the rotate tool rotates vertices for frame animations and sets rotation keyframes for bone joints in skeletal animations. The rotation of vertices is identical to the operation in model edit mode. The rotation of bone joints is different from model edit mode in that the rotation is always centered on the bone joint.

Keyboard shortcuts

  • R - Rotate

Scale

Overview

Scale tool icon
Scale selected vertices and faces.

Mouse operations

Press and hold a mouse button to begin a scale operation.

You can specify whether to scale from the far corner of the selected mesh or the center of the selected mesh by changing the Point combo box in the toolbar. By default the center point is used.

The amount of scaling is based on the position of the mouse relative to the scaling point, and the position of each selected vertex (vertices farther from the scaling point will scale more than vertices closer to the scaling point).

You can force the scale tool to preserve the aspect ratio of the scaled object in 2 or 3 dimensions using the combo box in the toolbar. By default the aspect ratio is not preserved.

You can force the scale tool to only scale in one dimension by holding the shift key. The first dimension the tool detects movement on will be the only one scaled. The shift key is ignored if you are preserving the aspect ratio through the combo box (input will only be counted on in one dimension, but 2 or 3 will be scaled by that input).

Animation mode

In animation mode the scale tool scales vertices for frame animations. It has no effect on skeletal animations.

Keyboard shortcuts

  • None

Shear

Overview

Shear tool icon
Shear selected vertices and faces.

Mouse operations

Press and hold a mouse button to begin a shear operation. The shear tool will find the bounds of the selected vertices. The nearest edge of the bounding region will be dragged in the direction of mouse movements. For example if the nearest bounding edge is the top edge, the top edge will move left or right while the bottom edge will remain stationary.

Horizontal edges move left or right. Vertical edges move up or down. Vertices between the moving edge and anchored edge will move proportionally based on their relative position between the two edges.

Animation mode

In animation mode the shear tool shears vertices for frame animations. It has no effect on skeletal animations.

Keyboard shortcuts

  • None

Extrude

Extrude tool icon
Extrude selected faces. This works like the extrude tool except that instead of entering a numeric offset for the location of the extruded faces, you drag the faces to the desired location with the mouse.

Mouse operations

Press and hold a mouse button to begin an extrude operation. The faces you select will move with the mouse. New faces will be created to connect the moving faces to the rest of the object. If you release the mouse button and press and hold again, it will start a new extrude operation.

Animation mode

This tool works like a create tool and has no effect in animation mode.

Keyboard shortcuts

  • None

Drag Vertex on Edge

Overview

Drag vertex tool icon
Drag Vertex on Edge of a triangle toward connected vertices.

Mouse operations

Press and hold a mouse button to begin a drag operation. As you move the vertex it will follow the closest edge of a triangle that the vertex belongs to.

Before

After

Animation mode

In animation mode the drag vertex tool moves vertices for frame animations. It has no effect on skeletal animations.

Keyboard shortcuts

  • None

Attract Near

Overview

Attract near tool icon
Attract Near translates selected vertices. Vertices closer to the mouse are affected more by the translation than far vertices.

Mouse operations

Press and hold a mouse button to begin an attract near operation. Drag the mouse to move the selected vertices. The nearest vertices will move the most. The farthest vertices will not move at all.

Animation mode

In animation mode the attract near tool moves vertices for frame animations. It has no effect on skeletal animations.

Keyboard shortcuts

  • None

Attract Far

Overview

Attract far tool icon
Attract Far translates selected vertices. Vertices farther from the mouse are affected more by the translation than near vertices.

Mouse operations

Press and hold a mouse button to begin an attract far operation. Drag the mouse to move the selected vertices. The farthest vertices will move the most. The nearest vertices will not move at all.

Animation mode

In animation mode the attract near tool moves vertices for frame animations. It has no effect on skeletal animations.

Keyboard shortcuts

  • None

Move Background Image

Overview

Move background image tool icon
The Move Background Image tool moves the center point of a canvas background image.

To set a background image for a viewport, use the Select Background Image Window.

Mouse operations

Press and hold a mouse button to drag the image to the desired position.

Keyboard shortcuts

  • None

Scale Background Image

Overview

Scale background image tool icon
The Scale Background Image tool increases or decreases the size of the canvas background image.

Mouse operations

Press and hold a mouse button to scale the image size up or down. Moving closer to the center will make the image smaller. Moving away from the center will make the image larger.

It is not possible to scale the X and Y dimensions independently.

To set a background image for a viewport, use the Select Background Image Window.

Keyboard shortcuts

  • None

Create Rectangle

Overview

Create rectangle tool icon
The Create Rectangle tool creates two triangular faces that form a rectangle.

Mouse operations

Press and hold a mouse button to begin creating a rectangle. The first corner of the rectangle will be located at the point of the mouse button press. Move the mouse to the location of the far corner and release the mouse button to finish creating the rectangle.

Animation mode

Create tools have no effect in animation mode.

Create Vertex

Overview

Create vertex tool icon
The Create Vertex tool creates a vertex that is not connected to any triangles.

Normally vertices that are created when triangles are created will also be deleted when triangles are deleted. A vertex created with the Create Vertex tool is a "Free Vertex". You can create a triangle connected to this vertex then delete the triangle later and the vertex will still be present. A "Free Vertex" will only be deleted if it is selected explicitly and deleted.

Mouse operations

Press a mouse button to create a vertex. If you hold the button down you can move the new vertex until the button is released.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Cube

Overview

Create cube tool icon
The Create Cube tool creates a set of faces in the shape of a cube.

When you select the create cube tool the cube tool bar will appear. You can use the cube tool bar to force the new cube to be cube shaped (the same width, height, and depth). You can also increase the number of faces that make up the cube by changing the value of segments. The segment value is N for an N by N by N cube. So a segment value of 3 would give you nine sets of squares (3x3) on each cube face (9 squares, 18 faces).

Mouse operations

Press and hold a mouse button to begin creating a cube. The first corner of the cube will be located at the point of the mouse button press. Move the mouse to the location of the far corner and release the mouse button to finish creating the cube.

The cube will be as deep as its narrowest side.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Ellipsoid

Overview

Create ellipsoid tool icon
The Create Ellipsoid tool creates a set of faces in the shape of an ellipsoid. The depth dimension of the tool will be identical to the shorter of the two radii (an ellipse is cigar-shaped, not plate-shaped).

When you select the ellipsoid tool the ellipsoid tool bar will appear. You can use the ellipsoid tool bar to increase the smoothness of the ellipsoid or make the ellipse a sphere (forcing the the radii to be the same in all three dimensions).

Mouse operations

Press and hold a mouse button to begin creating an ellipsoid. The ellipsoid will be drawn within the space you drag the mouse over, or with the center at the original click point, depending on the value of the From center check box in the tool bar. Release the mouse button to finish creating the ellipsoid.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Cylinder

Overview

Create cylinder tool icon
The Create Cylinder tool creates a set of faces in the shape of a cylinder. The cylinder is always drawn "horizontally" in the canvas view. The "width" is the length of the cylinder and the "height" is the diameter of the cylinder. Cylinders may be solid or hollow and may taper at one end.

When you select the cylinder tool the cylinder tool bar will appear. You can use the cylinder tool bar to increase the the number of segments (lengthwise) of the cylinder by setting the segments spin box. You can increase the the number of sides (around the cylinder) of the cylinder by setting the sides spin box. You can make a cylinder hollow by using the width spin box. The width is how think the cylinder is (as a percent of the radius). 100 is solid and 0 is an extremely thin cylinder. The scale is the size of one end of the cylinder relative to the other as a percentage of the total diameter. 100 is a cylinder, 0 is a cone with one pointed end.

Mouse operations

Press and hold a mouse button to begin creating a cylinder. The diameter of the cylinder will be the "height" (vertical distance) in the current canvas view. If the cylinder is cone-shaped, the smaller end will be where the drag began. Cylinders are always "horizontal" in the current viewport. You can use the rotate tool to change the orientation of a cylinder that has been created (holding down shift during rotation will allow you to rotate in 15 degree increments).

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Torus

Overview

Create torus tool icon
The Create Torus tool creates a set of faces in the shape of a torus. The torus is always drawn with the "hole" visible in the canvas view where the torus was created. You can increase the polygon count of the torus by changing the "smooth" value.

When you select the torus tool the torus tool bar will appear.

The width is width of the cylindrical portion wrapped around the center. 50 means the cylinder spans from the far outside of the torus to halfway to the middle of the torus. 100 means the cylinder spans all the way to the center of the torus. 199 means the torus will be almost spherical.

You can check the circle checkbox to force the torus to be circular.

Mouse operations

Press and hold a mouse button to begin creating a torus.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Polygon

Overview

Create polygon tool icon
The Create Polygon tool creates a triangle strip or fan. Each left click will create a new vertex for the polygon.

When you select the polygon tool the polygon tool bar will appear.

If the fan checkbox is checked the polygon will be a triangle fan. If the checkbox is unchecked, the polygon will be a triangle strip.

You can check the circle checkbox to force the torus to be circular.

Mouse operations

Press the left mouse button to create each vertex of the polygon. Press the right mouse button to invert the normals of the most recently created polygon.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Bone Joint

Overview

Create bone joint tool icon
The Create Bone Joint tool creates a bone joint connected to the nearest bone joint.

Mouse operations

Press a mouse button to create a bone joint. Hold the button down to move the joint where you want it. If there are no other joints the first joint you create will be a root joint. If there are other joints, the new joint will be connected to the closest existing joint (the existing joint will be the "parent" of the new joint).

The closest joint is the closest joint in the two dimensions of the orthographic canvas view you are creating the bone joint in. The third dimension (the depth) is not included in the distance calculation.

If you want the joint to be connected to a joint other than the closest one to the place where the joint will be positioned, create it near the parent joint and drag it to its target location.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Point

Overview

Create point tool icon
The Create Point tool creates a point. Points have a position and a direction. Points do not have a specific purpose for MM3D. They can be used as bolt points or other custom-purpose tags for model exporters or game engines. For example, the MD3 filter looks for specially named points to act as MD3 "tags".

You can use the rotate tool to rotate a point into a new direction, or explicitly set the rotation using the Rotation Properties Panel.

Points can be assigned to bone joints in the same manner as vertices.

Mouse operations

Press a mouse button to create a point. Hold the button down to move the point where you want it.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

Create Texture Projection

Overview

Create texture projection tool icon
The Create Texture Projection tool creates an object to project a texture onto faces as a sphere or cylinder. Texture Projections have a position and a rotation. Texture projections do not have a texture associated with them. The texture applied to faces is the texture assigned to the group to which the faces belong (in theory you could use the same texture projection to map two different textures onto two different sets of polygons).

You can change the type of projection to create by selecting the type from the Type combo box. You can also change the type of a projection after creating it using either the Texture Projections Window or the Projection Properties Panel.

Any faces that are selected when you create the projection will automatically be assigned to the projection.

Mouse operations

Press a mouse button to create a texture projection. Hold the button down to scale and rotate the projection to the size and orientation that you desire.

Animation mode

Create tools have no effect in animation mode.

Keyboard shortcuts

  • None

mm3d-1.3.15/doc/html/olh_tools.page000066400000000000000000000000561466047437300170310ustar00rootroot00000000000000PAGE_TITLE=Tools PAGE_CONTENT=Transform Model Window Transform Model Window allows you to apply a transformation Matrix to the model. There are tabs in the window for translation, rotation, and scaling, as well as a tab for a user-defined matrix. In some cases the Matrix you want to apply may not be undo-able. If this is the case, the window will warn you before performing the transformation.

Translate

In the Translate tab you can enter the X, Y, and Z components of the translation. Press the Translate button to perform the translation.

Rotate

In the Rotate tab you can enter a rotation as euler angles (rotation on the X axis, Y axis, or Z axis in degrees) or as a quaternion (a user-defined axis of rotation and an angle in degrees). Press the Rotate button to perform the rotation (you must press the button in the same frame as the rotation numbers that you entered).

Scale

In the Scale tab you can enter the X, Y, and Z components of the scale. Press the Scale button to perform the scale.

Matrix

In the Matrix tab you can enter any transformation matrix you want. For reference, the translation component is in the bottom row (as opposed to the rightmost column). Press the Apply Matrix button to apply the matrix to the model.

Press Close to close the window.

mm3d-1.3.15/doc/html/olh_transformwin.page000066400000000000000000000001051466047437300204150ustar00rootroot00000000000000PAGE_TITLE=Transform Model Window PAGE_CONTENT=MM3D Tutorial - Modeling Geometry


Getting Started

Introduction

This tutorial will demonstrate how to use Maverick Model 3D. The example model will be a humanoid character with texture maps and animations. The model will be aimed at an eventual MD3 export, because this demonstrates nearly all of Maverick Model 3D's capabilities.

Note that I am not an artist--neither 2D nor 3D, so my 3D model creation skills are somewhat limited. This fact should be obvious by the time you see the finished model. Also note that I am taking some short-cuts in the interest of producing this tutorial more quickly. There are several places where spending more time on detail would provide better results; but my goal was to finish this document rather than create a refined example of 3D art.

Meta Data

The first thing I'm going to do is open up the Model Meta Data Window from the Model menu. The Meta Data Window allows you to enter some non-rendered information about the model. In this case I'm just going to note my own name in a copyright string by clicking "New", then double-clicking on the row to edit the key and value.

In this instance the meta data is entirely optional. You can enter any sort of information you want in this window, or leave it completely empty. In some cases, meta data is used by import or export filters to track format-specific data that Maverick Model 3D's model class doesn't understand (for example, skin path information in MD3).

Background Images

Next I'm going to add a background image to my forward-facing viewport to aid in constructing the humanoid model. In this case I'll be using DaVinci's "Proportions". Often you'll want to use a reference object with images from multiple angles so you can get a better sense of width and depth.

The model is not intended to look exactly like DaVinci's work, it's just a rough guide.

The background image window has a tab for each viewport direction. In my case, I'm just selecting the Forward tab and selecting an image file.


Torso -- Creating Basic Shapes

Pick a Starting Shape

When creating a 3D object, you will usually start with one of the basic creation tools (cube, ellipse, cylinder, or torus). From there you can move vertices around to get the basic shape right and split edges or extrude to add detail.

For our humanoid model, a cylinder is roughly the right shape for all the large geometry components. Expect to see many of those.

Reshaping Geometry

I am starting with the torso, so I will create a cylinder for that. Cylinders are always created horizontally with respect to the viewport in which they are created. So after creating this cylinder I need to rotate it vertically. If you hold the shift key down while rotating, the rotate tool will rotate in 15 degree increments. This makes it very easy to get the cylinder perfectly vertical.

Now I want to use the Front viewport background image as a reference to relocate vertices. The selection tools allow you to select portions of the model. There are selection tools for vertices, faces, material groups, connected meshes, etc. You start a selection operation with the mouse button, and use the bounding box to select the region that contains the geometry you want to select.

The left mouse button selects geometry, the right mouse button unselects. If you press the left mouse button alone, all selected portions of the model will automatically be unselected. If you hold the shift button the current selection will remain selected. The right mouse button never clears the current selection.

I use the Select Vertices tool to select the vertices for the upper body and use the Scale Tool to resize that section all at once. Then I select vertices down the sides and move them to match the outline of the image.

Next I use the side viewport to adjust the vertices in the Z (depth) axis, which isn't possible from the Front view. In this case the X (width) axis and Y (height) axis are correct. So I want to be careful to only move the vertices left or right in the Side viewport. If I hold down the shift key while using the Move Tool, the move tool will only move on one axis--the first axis it detects motion on.


Torso -- Detail Adjustments

Edge Turn

Of course vertices are not the only things to be concerned with when shaping the geometry. The faces themselves need attention too. Note the highlighted faces near the base of the torso in the image. The edge where the faces meet forms a concave angle on the outside of the geometry. A convex angle would look more correct. We can fix this with an Edge Turn. This rotates the edge from the two shared vertices of a face to the two opposite vertices.

If you do any significant work in a 3D model editor, you will make regular use of the Edge Turn feature. If you are just starting out with 3D modelling and the faces look wrong even though all the vertices seem to be in the right place, it is probably because you need some edge turns. Occasionally you'll need a sequence of edge turns. Note that edge turns do not preserve texture coordinates, so you'll want to make sure your geometry is mostly where you want it to be before you starting applying a texture map to it.

Edge Divide

The basic torso shape is in place, but it looks a bit angular on the sides. To fix this I'm going to split faces by doing a series of Edge Divide operations. If you have two vertices selected, and do an Edge Divide, it will add a vertex between the two vertices and split any triangles that use that edge into two triangles.

I mentioned above that you select the vertices of the edge to split. Selecting the edge would be more intuitive, but edges are not first-class objects in Maverick Model 3D, so there isn't a way to select the edge. However, you can select a face that uses the edge and then unselect the odd vertex that isn't on the edge you want to split (use the shift key with the select tool to unselect selected geometry).

Snap, Weld, and Hide

I'm splitting some edges, but I'm also welding some vertices back together. Welding vertices causes two or more vertices selected vertices that are near each other to become one vertex. This may cause some triangles to be removed implicitly (if a triangle has an edge with two vertices that are welded, the triangle becomes invisible and is unecessary). To get the vertices lined up I'm using the Snap to Vertex feature. Alternatively you could use the Snap Together command. The Snap Together command has welding and non-welding versions.

While I am working on one side of the model, faces from the other side are visible in the same viewport and are distracting, so I use the Hide command to make that portion of the model invisible. While hidden, the geometry still exists but it is not drawn and it will not be modified except under some specific circumstances. Some hidden faces may share vertices with unhidden faces. If you move those shared vertices the hidden faces will be modified.

Boolean Operations and Flipping

Now that one side looks good I want to make the other side match it. I could repeat all the work I did on the opposite side, but I'd rather not go through that effort. What would be easier would be to cut the torso in half, duplicate the side that I've finished, and then mirror it so that both sides are identical.

Here I use the Boolean Operation panel to cut the torso in half (read more about boolean operations if the following doesn't make sense). I will set the torso as Object A and subtract a cube from the torso. I use the Simlify Mesh command to remove faces that don't add detail.

Then I remove the faces that will be inside the torso since they won't be visible. Finally I Duplicate the half of the torso and Flip it. I select the vertices down the middle and use the Weld command to join the torso together again. Note that weld only welds vertices that are extremely close, so I can weld all the verticies in one operation.

If you're thinking that this use of Boolean Operations is a bit contrived, I agree. Boolean operations can work with much more complex objects than what I have shown here, but this works well as a basic example.


Torso and Legs -- Combining Shapes

I'm ready to start working on the legs. Before I do that I want to adjust the depth of the torso. Assuming that the depth of the torso is about the same as the width of the upper leg, I'll use the same background image in the side view as the front view, then I'll scale the depth of the torso until it looks about right.

For the legs, we'll start with the left leg and plan to duplicate it. First I create a cylinder, this time with more sides since I ended up increasing that with the torso manually. Again, the tool creates the cylinder horizontally, so I shift-rotate it to vertical. Then I'll scale it to be roughly the right proportions for the leg reference.

Now I connect the leg to the torso. I remove the faces on the bottom of the torso and the top of the leg where the two will connect. I use snap to vertex with the move tool to connect the vertices. Then I use weld to fuse the two meshes into one.

I need to do an edge turn to make the angles look right. Finding the right faces to select in the orthographic side and front projections can be tricky. So instead, I flip the perspective view into an orthographic projection and use that projection to select the faces. Then I flip back to perspective to make sure the right faces are selected. Finally I do the edge turn.

Now I move the leg vertices in the side view to match the leg in the background image. Note that the reference image has the leg twisted to one side, so this should be pretty close to the expected final results. The process for using the background image as a reference for the vertices is the same as for the torso.

Now I need to add a foot. Rather than creating a separate piece of geometry and welding it on, I will extrude existing faces on the leg into a foot. The extrude feature is available as an interactive Extrude Tool and an Extrude Geometry Command. I will be using the tool version here.

First I extrude the bottom faces to make segments for the ankle and heel. Before extruding the foot itself I move some toe-side leg vertices up to the ankle and use Snap Nearest and Weld. Then I extrude the front faces on the ankle/heel to make the foot. Finally I move the foot vertices around a bit to make the foot look more... foot-like.

Zooming out a bit, the leg looks somewhat curvy. I adjust the leg vertices to take some of the curve out.

Now that the leg is in pretty good shape, I want to duplicate it for the right side. This is just a simple matter of select, duplicate and flip. Because the torso is symmetrical (and I didn't move the torso vertices to connect the leg) all I need to do to fuse the meshes together is select the vertices on the seam and weld them together.

Now that I have two legs, the model looks pigeon-toed. So I'll adjust that now.

mm3d-1.3.15/doc/html/olh_tutorial_index.htm000066400000000000000000000002341466047437300205750ustar00rootroot00000000000000

MM3D Tutorial - Table of Contents

Sorry, I still haven't written this tutorial.

mm3d-1.3.15/doc/html/olh_tutorial_index.page000066400000000000000000000001061466047437300207170ustar00rootroot00000000000000PAGE_TITLE=MM3D Tutorial - Index PAGE_CONTENT=Vertex Details

Overview

A vertex is a geometric point. A model triangle (or face) is defined by the three vertices that are connected to form the triangle. Triangles may share vertices. In general vertices are created when a tool is used to create triangles and deleted when all the triangles that use the vertex are deleted. However, it is possible to create a vertex independant of any triangle.

Creating a Vertex

A vertex is created implicitly when triangles are created. Select any tool that creates a geometric shape (such as a cube, sphere, or cylinder) and it will create new vertices for the triangles that it creates. You may also create a vertex without any triangles attached by using the Create Vertex tool.

Note that a vertex that is created implicitly will be deleted implicitly when all the geometry that uses it is deleted. By contrast, a vertex that was created with the Create Vertex tool must be explicitly selected and deleted by the user. To find unused vertices, see the Select Free Vertices command.

Other Vertex Details

If two triangles share a vertex and you want to force them to each have a separate vertex, you can use the Unweld Vertices command. If you want to combine two vertices into one, use the Weld Vertices command. command.

In order for weld to work, the vertices must be very close in 3D space. If the vertices are too far apart you can force them together by using the Snap Together command. You can also enable the Snap To Vertex feature to make it easier to line up vertices with each other.

Vertices can be attached to one or more bone joints to animate geometry in animation mode.

See Also

mm3d-1.3.15/doc/html/olh_vertexdetails.page000066400000000000000000000000761466047437300205560ustar00rootroot00000000000000PAGE_TITLE=Vertex Details PAGE_CONTENT=Viewport Settings

The Viewport Settings Window is used to change the grid display in the model viewports. Select Viewport Settings from the Main Window's View menu.

The Color Scheme type specifies the colors for the model viewport background. Automatic will try to detect if the operating system is configured to use light or dark mode. The Light and Dark options will set the color scheme directly.

The Default Grid Unit field specifies the default distance between each grid line. There is a separate setting for the canvas and 3D perspective viewports. The number of actual lines drawn depends on the zoom factor of the viewport (the grid spacing doubles or halves as needed).

For the perspective viewport you can specify the number of grid lines that are drawn (in each dimension) using the Grid Lines field. You can turn grid lines on and off for each plane independently using the Plane checkboxes.

Press Ok to keep your changes or press Cancel to ignore any changes.

mm3d-1.3.15/doc/html/olh_viewportsettings.page000066400000000000000000000001041466047437300213230ustar00rootroot00000000000000PAGE_TITLE=Viewport Settings PAGE_CONTENT= What's New in MM3D?

Below is a list of new features in version 1.4.

  • General:
    • 64-bit support
    • Translation support
    • Context-sensitive panel for editing properties of selected objects
    • Export selected portions of a model to a separate file
    • Boolean Operations
    • Global transformations (works on entire model, including all animations)
    • Texture projections (cylinder/sphere mapping)
    • Direct vertex coordinate editing
    • Orthographic projections can be rotated to any view angle
    • Flip between orthographic and perpective projections
    • Rotate a viewport on the Z axis
    • Save/restore viewing angle and zoom with keyboard shortcuts
    • Render selected faces in red (instead of rendering just the edges in red)
    • Numerous texture coordinate window improvements
  • Animation / Bone Joints:
    • Converted the animation window to a dockable toolbar
    • Support for multiple bone joint influences on a single vertex
    • Auto-assign bone joint command
  • New Tools:
    • Snap to Grid/Vertex
    • Bolt points
    • Extrude (viewport interactive version of the geometry command)
  • New Commands:
    • Edge turn
    • Edge divide
    • Cap Holes
    • Simplify Mesh
    • Face Out
  • File formats:
    • MD3
    • Cal3D
    • COB
    • DXF
    • IQE
    • SMD
    • D3D
mm3d-1.3.15/doc/html/olh_whatsnew.page000066400000000000000000000000761466047437300175330ustar00rootroot00000000000000PAGE_TITLE=What's New in MM3D? PAGE_CONTENT= mm3d-1.3.15/doc/html/secleft.htm000066400000000000000000000001751466047437300163320ustar00rootroot00000000000000
mm3d-1.3.15/doc/html/secright.htm000066400000000000000000000000621466047437300165100ustar00rootroot00000000000000
mm3d-1.3.15/doc/html/template.htm000066400000000000000000000020051466047437300165120ustar00rootroot00000000000000 Maverick Model 3D Help - <TMPL_VAR name="PAGE_TITLE">
[ Contents | Introduction | Tools | Geometry Menu ]
Copyright © 2004-2008, Kevin Worcester
Copyright © 2015-2024, Zack Middleton
mm3d-1.3.15/doc/html/tutorial_outline.txt000066400000000000000000000073451466047437300203440ustar00rootroot00000000000000Meta Data Copyright Add background image Front - proportions Start modeling Cylinder, 4 segments, six sides Have to rotate (hold shift) to get it vertical select vertices and scale to chest size move vertices to match drawing Adjust dimensions in other viewports (but only adjust on the depth axis) edge turn at waist see problem edge, highlight select edge turn see corrected faces edge divide to add detail snap to vertex see also drag vertex hide back faces are getting in the way select, hide selected edge turn All down the side (single before and after picture) unhide Mirror Use boolean operation to cut it half Torso is object A Cube is object B Subtract B from A Simplify mesh Duplicate (note vertex count) Flip Select center vertices weld (note vertex count) Use proportions image in Right viewport Just for depth reference on next to leg Cylinder for leg 6 segments, 8 sides horizontal, rotate to vertical scale to match leg width Connect Remove faces under torso where leg will connect Remove top faces above leg where it will connect with torso snap to vertex to join vertices weld to connect Need to edge turn Finding exact faces in side view can be tricky Switch perspective to ortho and select faces that way Flip back to perspective Do edge turn and see result Move vertices to match up with leg (in side view) Make foot Select bottom faces to make ankle and heel Extrude tool Move toe-side leg vertices up to ankle top, snap nearest and and weld Extrude again to make foot Move vertices to match up with leg (in front view) Leg is kind of curvey, doesn't look natural Go back up leg and adjust vertex postions Duplicate leg, flip, select seam vertices, and weld Make less pigeon toed (100 to 101) Make arm 8 segments, 8 sides, horizontal, don't need to rotate this time Match vertices to reference in front view Extrude where arm will meet shoulder Connect Rotate arm on X axis so that front view is now facing down (palm down) Remove shoulder and arm internal faces in preparation for weld Hide legs, arm, and far side of torso to get a better view Move vertices (snap to vertex for move tool) and weld Adjust vertices from front view again to make the cylinder more arm-shaped Looks kind of octogony, weld some vertices to make the shape look less blocky Make hand Extrude end of arm for hand Extrude side faces to make thumb Move vertices and weld to make thumb look less blocky Add smoothness by splitting faces Weld split faces to reconnect Do some edge turns on the thumb Arm looks good enough for now Don't want to duplicate and connect on to the shoulder on the other side Just delete half of torso, dup and flip along with arm Select all and weld (only near vertices are welded) Head Extrude torso top to make neck Remove neck top faces to make room to attach head Cylinder, 6 segments, 8 sides Horizontal, rotate it Use reference image to scale to basic head size Create visor by moving vertices, edge turning as necessary Shape back of helmet Adjust vertices to match head shape (in Front view) Edge divide and move vertices to create breathing mask area Face shield, duplicate and extrude to create inset Hide helmet to show what it looks like Face shield will be transparent, insde will be visible Connect neck to torso at shoulders Completed geometry (1x1 view) (lacks chest shield) mm3d-1.3.15/install-sh000077500000000000000000000357761466047437300145050ustar00rootroot00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: mm3d-1.3.15/man/000077500000000000000000000000001466047437300132325ustar00rootroot00000000000000mm3d-1.3.15/man/Makefile.am000066400000000000000000000002321466047437300152630ustar00rootroot00000000000000EXTRA_DIST = \ mm3d.1 all: install: $(INSTALL) -d $(DESTDIR)$(datadir)/man/man1/ ${INSTALL} -m 0644 $(srcdir)/mm3d.1 $(DESTDIR)$(datadir)/man/man1 mm3d-1.3.15/man/mm3d.1000066400000000000000000000034611466047437300141600ustar00rootroot00000000000000.TH MM3D 1 "May 31, 2007" .SH NAME mm3d \- Maverick Model 3D .SH SYNOPSIS .B mm3d .RI [ options ] " files" ... .br .SH DESCRIPTION This manual page documents briefly the .B mm3d command. .PP \fBmm3d\fP is an OpenGL\-based 3D model editor that works with triangle\-based models. It supports multi\-level undo, skeletal animations, simple texturing, scripting, command\-line batch processing, and a plugin system for adding new model and image filters. Complete online help is included. It is designed to be easy to use and easy to extend with plugins and scripts. .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. For a complete description, see the Info files. .TP .B \-h, \-\-help Print command line help and exit .TP .B \-v, \-\-version Print version information and exit .TP .B \-b, \-\-batch Batch mode (exit after processing command line) .TP .B \-s, \-\-save Save command line changes to model files .TP .B \-\-convert [format] Save models to format [format] .TP .B \-\-no-plugins Disable all plugins .TP .B \-\-no-plugin [foo] Disable plugin [foo] .TP .B \-\-sysinfo Display system information (for bug reports) .TP .B \-\-debug Display debug messages in console .TP .B \-\-warnings Display warning messages in console .TP .B \-\-errors Display error messages in console .TP .B \-\-no-debug Do not display debug messages in console .TP .B \-\-no-warnings Do not display warning messages in console .TP .B \-\-no-errors Do not display error messages in console .SH SEE ALSO .BR blender (1), .BR wings3d (1). .SH AUTHOR mm3d was written by Kevin Worcester, and is now maintained by Zack Middleton. .PP This manual page was written by G\[:u]rkan Myczko , for the Debian project (but may be used by others). mm3d-1.3.15/missing000077500000000000000000000153361466047437300140660ustar00rootroot00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2020 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: mm3d-1.3.15/mkinstalldirs000077500000000000000000000066721466047437300153000ustar00rootroot00000000000000#! /bin/sh # mkinstalldirs --- make directory hierarchy scriptversion=2020-07-26.22; # UTC # Original author: Noah Friedman # Created: 1993-05-16 # Public domain. # # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' IFS=" "" $nl" errstatus=0 dirmode= usage="\ Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... Create each directory DIR (with mode MODE, if specified), including all leading file name components. Report bugs to ." # process command line arguments while test $# -gt 0 ; do case $1 in -h | --help | --h*) # -h for help echo "$usage" exit $? ;; -m) # -m PERM arg shift test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } dirmode=$1 shift ;; --version) echo "$0 $scriptversion" exit $? ;; --) # stop option processing shift break ;; -*) # unknown option echo "$usage" 1>&2 exit 1 ;; *) # first non-opt arg break ;; esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and # mkdir -p a/c at the same time, both will detect that a is missing, # one will create a, then the other will try to create a and die with # a "File exists" error. This is a problem when calling mkinstalldirs # from a parallel make. We use --version in the probe to restrict # ourselves to GNU mkdir, which is thread-safe. case $dirmode in '') if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" else # On NextStep and OpenStep, the 'mkdir' command does not # recognize any option. It will interpret all options as # directories to create, and then abort because '.' already # exists. test -d ./-p && rmdir ./-p test -d ./--version && rmdir ./--version fi ;; *) if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "umask 22" umask 22 echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" else # Clean up after NextStep and OpenStep mkdir. for d in ./-m ./-p ./--version "./$dirmode"; do test -d $d && rmdir $d done fi ;; esac echo "umask 22" umask 22 for file do case $file in /*) pathcomp=/ ;; *) pathcomp= ;; esac oIFS=$IFS IFS=/ set fnord $file shift IFS=$oIFS for d do test "x$d" = x && continue pathcomp=$pathcomp$d case $pathcomp in -*) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp=$pathcomp/ done if test ! -z "$dirmode"; then echo "chmod $dirmode $file" chmod "$dirmode" "$file" || errstatus=$? fi done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: mm3d-1.3.15/mm3d-flatpak.yaml000066400000000000000000000052701466047437300156270ustar00rootroot00000000000000# # Flatpak manifest # # For building the flatpak, first ensure flathub repo is added for installing the KDE runtime. # # flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo # # To build and install the Flatpak (requires flatpak and flatpak-builder programs): # # flatpak-builder --user --install --install-deps-from=flathub --force-clean --state-dir=flatpak-state flatpak-build mm3d-flatpak.yaml # # After it's installed you can remove "flatpak-state" and "flatpak-build" directories. # # rm -r flatpak-state flatpak-build # # To uninstall the Flatpak use: # # flatpak uninstall --user moe.clover.mm3d # # # Releases are built labeled as "stable" branch using the following: # # flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo # flatpak-builder --default-branch=stable --user --install-deps-from=flathub --force-clean --repo=flatpak-repo --state-dir=flatpak-state flatpak-build mm3d-flatpak.yaml # flatpak build-bundle flatpak-repo moe.clover.mm3d-$(git describe|sed 's/^v\([0-9]\)/\1/').flatpak moe.clover.mm3d stable # # The release can be installed using: # # flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo # flatpak install --user flathub org.kde.Platform/x86_64/5.15-23.08 # flatpak install --user -y moe.clover.mm3d-*.flatpak # # app-id: moe.clover.mm3d runtime: org.kde.Platform runtime-version: '5.15-23.08' sdk: org.kde.Sdk command: mm3d finish-args: # Qt has issues on Wayland in 5.15-23.08 SDK (only tested on Gnome) # * The main window content size and/or position flickers. # * Model viewport flickers between old frame sometimes. # * Opening, closing, and reopening Materials -> Texture Coordinates... # causes the texture to be transparent after clicking it until # interacting with some other part of the window. - --socket=x11 # OpenGL access - --device=dri # Wayland access #- --socket=wayland # X11 + XShm access - --share=ipc #- --socket=fallback-x11 # File access - --filesystem=host modules: # glu from flathub/shared-modules - name: glu buildsystem: meson sources: - type: archive url: https://archive.mesa3d.org/glu/glu-9.0.3.tar.xz sha256: bd43fe12f374b1192eb15fe20e45ff456b9bc26ab57f0eee919f96ca0f8a330f cleanup: - "/include" - "/lib/*.a" - "/lib/*.la" - "/lib/pkgconfig" - name: mm3d config-opts: # specify where GLU was just installed to # NOTE: "-L/app/lib" is set in LDFLAGS by flatpak-builder but mm3d configure doesn't use it for testing GL LIBS. - "--with-GL-cflags=-L/app/lib" sources: - type: dir path: . mm3d-1.3.15/mm3d-win32-installer.nsi000066400000000000000000000145171466047437300167750ustar00rootroot00000000000000; Maverick Model 3D - NSI Script (Installer Script) ; ; Copyright (c) 2004-2007 Kevin Worcester ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, ; USA. ; ; See the COPYING file for full license text. !define VERSION "1.3.15" !define FILE_VERSION "1_3_15" Name "Maverick Model 3D ${VERSION}" OutFile "mm3d-${FILE_VERSION}-win32-installer.exe" SetCompressor lzma Icon mm3d.ico UninstallIcon mm3d.ico BrandingText "Maverick Model 3D" CRCCheck on XPStyle on Unicode true InstallDir "$PROGRAMFILES\Maverick Model 3D" InstallDirRegKey HKCU "Software\MaverickModel\Maverick Model 3D" "INSTDIR" LicenseData COPYING Page license Page components Page directory Page instfiles UninstPage uninstConfirm UninstPage instfiles Section "Maverick Model 3D" SectionIn RO ; Create file type WriteRegStr HKCR "MaverickModel.Mm3dModelFile" "" "MM3D Model File" WriteRegStr HKCR "MaverickModel.Mm3dModelFile\shell\open\command" "" '"$INSTDIR\mm3d.x86.exe" "%1"' WriteRegStr HKCR "MaverickModel.Mm3dModelFile\shell\edit" "" "Edit Model" WriteRegStr HKCR "MaverickModel.Mm3dModelFile\shell\edit\command" "" '"$INSTDIR\mm3d.x86.exe" "%1"' SetOutPath "$INSTDIR" WriteRegStr HKCU "Software\MaverickModel\Maverick Model 3D" "INSTDIR" "$INSTDIR" File /oname=COPYING.txt COPYING File build\mingw32-x86\install\mm3d.x86.exe File /r build\mingw32-x86\install\doc SetOutPath "$INSTDIR\translations" File build\mingw32-x86\install\translations\*.qm SetOutPath "$INSTDIR" CreateDirectory "$INSTDIR\plugins" CreateDirectory "$INSTDIR\plugins\1.3" ; Qt dlls File build\mingw32-x86\install\Qt5Core.dll File build\mingw32-x86\install\Qt5Gui.dll File build\mingw32-x86\install\Qt5Svg.dll File build\mingw32-x86\install\Qt5Widgets.dll SetOutPath "$INSTDIR\iconengines" File build\mingw32-x86\install\iconengines\*.dll SetOutPath "$INSTDIR\imageformats" File build\mingw32-x86\install\imageformats\*.dll SetOutPath "$INSTDIR\platforms" File build\mingw32-x86\install\platforms\*.dll SetOutPath "$INSTDIR\styles" File build\mingw32-x86\install\styles\*.dll ; Qt translations SetOutPath "$INSTDIR\translations" File build\mingw32-x86\install\translations\*.qm SetOutPath "$INSTDIR" ; Qt MinGW compiler run-time File build\mingw32-x86\install\libgcc_s_dw2-1.dll File build\mingw32-x86\install\libstdc++-6.dll File build\mingw32-x86\install\libwinpthread-1.dll ; Additional dlls used by Qt (mm3d requires desktop OpenGL so they aren't useful) ;File build\mingw32-x86\install\D3Dcompiler_47.dll ;File build\mingw32-x86\install\libEGL.dll ;File build\mingw32-x86\install\libGLESV2.dll ;File build\mingw32-x86\install\opengl32sw.dll WriteUninstaller "Uninstall.exe" SectionEnd Section "Start Menu Shortcut" ; SetShellVarContext all CreateShortcut "$SMPROGRAMS\Maverick Model 3D.lnk" "$INSTDIR\mm3d.x86.exe" SectionEnd SubSection /e "Associate file types" Section "MM3D (Misfit Model 3D)" WriteRegStr HKCR ".mm3d" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "Cal3d" WriteRegStr HKCR ".cal" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "COB (Truespace)" WriteRegStr HKCR ".cob" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "D3D (GameMaker)" WriteRegStr HKCR ".d3d" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "DXF (Autocad DXF)" WriteRegStr HKCR ".dxf" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "LWO (Lightwave)" WriteRegStr HKCR ".lwo" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "MS3D (Milkshape)" WriteRegStr HKCR ".ms3d" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "MD2 (Quake)" WriteRegStr HKCR ".md2" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "MD3 (Quake)" WriteRegStr HKCR ".md3" "" "MaverickModel.Mm3dModelFile" SectionEnd Section /o "OBJ (Alias Wavefront)" WriteRegStr HKCR ".obj" "" "MaverickModel.Mm3dModelFile" SectionEnd SubSectionEnd Section "Uninstall" Delete "$INSTDIR\COPYING.txt" Delete "$INSTDIR\mm3d.x86.exe" Delete "$INSTDIR\translations\*.qm" ; Qt dlls Delete "$INSTDIR\Qt5Core.dll" Delete "$INSTDIR\Qt5Gui.dll" Delete "$INSTDIR\Qt5Svg.dll" Delete "$INSTDIR\Qt5Widgets.dll" Delete "$INSTDIR\iconengines\*.dll" Delete "$INSTDIR\imageformats\*.dll" Delete "$INSTDIR\platforms\*.dll" Delete "$INSTDIR\styles\*.dll" ; Qt translations Delete "$INSTDIR\translations\*.qm" ; Qt MinGW compiler run-time Delete "$INSTDIR\libgcc_s_dw2-1.dll" Delete "$INSTDIR\libstdc++-6.dll" Delete "$INSTDIR\libwinpthread-1.dll" ; Additional dlls used by Qt ;Delete "$INSTDIR\D3Dcompiler_47.dll" ;Delete "$INSTDIR\libEGL.dll" ;Delete "$INSTDIR\libGLESV2.dll" ;Delete "$INSTDIR\opengl32sw.dll" Delete "$INSTDIR\Uninstall.exe" RMDir /r /REBOOTOK "$INSTDIR\doc" RMDir /REBOOTOK "$INSTDIR\translations" RMDir /REBOOTOK "$INSTDIR\plugins\1.3" RMDir /REBOOTOK "$INSTDIR\plugins" ; Qt directories RMDir /REBOOTOK "$INSTDIR\iconengines" RMDir /REBOOTOK "$INSTDIR\imageformats" RMDir /REBOOTOK "$INSTDIR\platforms" RMDir /REBOOTOK "$INSTDIR\styles" RMDir /REBOOTOK "$INSTDIR\translations" RMDir $INSTDIR ; SetShellVarContext all RMDir /r /REBOOTOK "$SMPROGRAMS\Maverick Model 3D" DeleteRegKey HKCR "MaverickModel.Mm3dModelFile" SectionEnd mm3d-1.3.15/mm3d.icns000066400000000000000000002530251466047437300142040ustar00rootroot00000000000000icnsVic089PNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o5IDATxYmZ7"D0&^;0cs# 1AM4 /sri#x#?0I߉ZOջ=\sUj5Hj}xf99ku t:@g3 t:@g3 t:@g639qY/'./}{u:;w:o+_89|rvI;ךy,Gsx݅φ~V_5]S5 ).G 0/} H{'i`Woٵ  |b)k[ϻ>oLw` /&3Rw ;gNt'rqIM獇%[0?5cBJ񧟛{ ԟO? :Ȁ ]ktyvӧ76uwwI+>L<ؿN<")ߙm.I./3x\'Of"73, w{'Ν"wzO5L[Prǯutꑹ'/w};KnF|˼߅PNv$ybuzkz'5%ut#Y,b.?^#qß.7|}i,oҽ qO6Nvᑉ9SIf9Ěű96F=˳ӏ*Odq.aog >x'uf?ɾ}M'=2' PX s0~ üf})'/O߲lz~qpGrѯboݙ̏;ĶK y$_\ oӀpa \"E>ட{pܽiÍ]O~xM]&~nLz/-8OKw>_*Ջ.u,K\b~؏t#c *F:ap /ObYrԓۇGz8&3b';R' \~z/?{9±uO:eEl >6tm'zomb'+z깻#[8o ԓ+:2z侢h'W^\u@͓}? X,?9%}3Stޅˏ9L^qJ'5|rCh́EPK}69f Cԓ#6EϞH\c}].t:1O^iOm`p4\w]<J9vwmS~MqW' ۺ#G;\# %!_%ǁK A.=e 3^-HiN߲ln)K}bsoOѶֵyt&ùKW=zq!ڮo;׋}Pi?ެ{\6^ȭ,y[ 9SS~9ysRd'n匁sCxS܂i2{:tbDO\'}+Lt9<бPX~=>T -wgോ /OⰓ>}D+>R\E ?!R\>l''],'wwG@> ~>gy:\.bJp< NV5Ё;̀{I6>opC:ygWl ];6:8mi '8m'kmb|b@ v'Oj.1gmġT ] }Smv9ai|Ϥ-uW9ofk Y9>[1rS{?c:k|cP#s{i֪\ OYLb}."ݪ٫Ձ:@g3p}ov~_w}OWL+ʝ<|bʥmڈS&N>pQcޝ7glO6b&.t@g3'_Xvw~cN^Hʝw)Nۊu$qg$#~bsmmR3[䑎6<%ĩ @_%| v0*O*7<2ybTN[W?r}Nms=;gr1`,,w& v]UxUv t3n:gkHޟio^?>+?@ʃ_X]sq3?Xxj/ɭat[Iz玮רڪ^_bQxo~rb'IYώcxgObfI]1~@py G6[lO2c mQWl<'91m?#%y;9Ŵe{H~>>cma- N>}w t,}8%5{7ѹם~)f'#sgqg KY dI;ywE/]bwo^C;W("OL8_~s2A6rj3~cg{ OkhȼHq&N}bm\B>%?e$Eا>I @;ӎ2><2cxop̱JX1;Ng3p%p^f7@r]SìʮJvwpZc%6ι;¦Lm0蓧+rkQg:Hʚ凟z~b'1rgvB m"㇞J4ɓZ囂v}u;kD;~h"2cKI5X>[W|UV۬;9.붱Eg\}w;&y$MjӞ l?p$i8i@gF2u#s]YQ]UY]SGbTFS7i#Vb_qgLtkz+jk#ў:%YxH=8}OL]6ۆ^C/asbf]rF&?{\;k赙ظ(Rl tn$}8p M+7;GJW]d#lЍk|gUGUmᬫ>916pbZR_Wb'z2mx "< x PpoS #u\uӯb*g^=r_nG\Dxn;u䴫|K%z1;ھ2qHɎ$۶ tn,.7v/?^y`v%E&vO'K=,Fn#Xշg<8vI9wLxZGκz,od[ڧ-mW}x>Կy4a`Iq0 JWi^?t媞:j_jXj.O[T9s%8mİ_k(']-yoְ`/mcŤUf'|E WZ++)܌bЍlӯb[cKIGf0>ĵ:XUgi '6~Kjo}~p{)&I½z=1o5<Dg/&E;7bmr)+UWmk_hޢmrX"k!V/qƟiS1>Y2FbmᑭRwf >^3&^@gN3FeOMI vWl uJ} ~ƶm=S?i&l`QmfxևC}q?8#i@g3v˫OsO++mr[1٢#3L3ljInIH١swML8Y6KU1HceAz_#VzXN ɯ݁H>~H1˥>q^-zJJcֺQ_)G~Ԗ.Ɖ/g]N1ӏlI}Fzuȼﰭ wV'Ag3p?XypŬo3LSĵnwSqic}1F8cp Wʥ>16YF3^bK~&g~suYfޯ$POXx~)сLj{_3 }Su`'8vfHk6c'1O^n$Ӗ`d :7 pzX\NITZw=z̰3zKb*WkLXiIƥnOoUzZluHO_ 2}Gϳ($A drɋ MO?M>qƃ_+i[:-Fr=iS1vG:^az}7}viC罪]@g2pugߗԓG&^uY'4Hjf\껦{mƬ;um֌O}Q7xɉG[X}G?^D>CoId;0U&kQ}6k[u_}zbc?GpmMnXS~闲7mFx~&@g2'2l}?-n#5]OL1M?6On*gS}oicRɘpa_&v}zg2'2خE:Zٷr[ Vep3Y:񾶱bc<%>#eMo?3{4~0l]ڊ"ߓk>. XZY߶l FU7z/)GqgꍻGj-}f띁;@? !+u&fK=X[Wym:8V;ӖzUV+jŕnkoۏ!g}ZtdbeHߏdKgN3pdUs;ѕ⣟2*Wi Va}rŗC7ӧ xdglg<~?zRON4>i=5O?̶Yv7 P)wg-vdGmgv)?3nW~O5KG׌M?.;@VUSbV{%8uk?}>Q7'ecdc\uǧ>L^H1g,u'?>rֳ Y;/G?SX:vp+6uAyhZW#h `e]=s_1߬2n:'$4ƞ鉝6mwX~x('~˺p(U#5oleW'w(}XlWMdRi5vɭP?A;;>3N[k^mSt[M''Vҗ>>)yx2}*ζz/LxENxXRxڨե?x q\b%vଧXk5U5?T]J첽ԉL a|x)uV_u8h2]-6~g{$'&9xz#Y?XH_Z7nͰUO?L >jICN fr3j[k}G^Y\;[dzbeZGb7NY.eulSG/wH?c\|8#Q1pRжk_c^N}uԖzG|r\ӏtɉg=۪j_rO&Orb& ',_~d4 [0qV\WĮHq˧+m&tc؇:m:Mgܪ&x~WκR?Jy]&&7jr&/+%cr0NRc:mUnz|k,!)bMXG&?_yÎ/)pڋyPȖ{̀'W^;xe'p{T+u+Uz'/3% }$|i~WO&Kկ٦~疣Nz~{#-eg3pM1|RĮ=Fr3[K>7W[3Xd岮uiK}쳝UoY[O[uUlӯ3B^D{ 0NCSw 码DI7Balkqr>i>u}6Y3Saj&OƭݗnV,pԮ#Gi3pG`E 寿[WeW\Җ2m;cƳxWR}:>K&~.}a5۴Qs,>qo$=2hwc%8HN9CcUӂ^{9d%??3*O762}H.qg<6R!#y?Amo|fOW/+1/])]uS_wdw%6]ʝ6HlqTW>)5O>N~ mڌ .c%}'< |.ȏIG:#NX!S''V]bJuW=LOG*Տ|zb3lgq(ɝ 0M̆ Å|Vun=GxxW_M~y |ѣxZRS~'%lX{ڱ'O.V$^IP7S7OHmKOH bbs3g{>1EoMaҕ_$$A ILT\el?GG>bkCy#FXCuiOc1F뷥W:S,<fߺ@g2'C/} ,?U9WqWplF?ڔN`G]=u1z)꡸3+g_cP7:R8[G&67pڦ }DiH1v5[_{֟]eOw p@] Mw'D&vG;OnQ?rw϶刕XYyg?W>7NbSd?2N^^b̯;H.\N-8F7 wa+NLAVOA7!SoknVlbs='`~Om3F/m?}8tg=j@g2'Gw/2KwB'GWvKix>=2a_%:y娋S=D~#LLlͩ?197R?yboCL(y!2H3Ndbo@'pihc=QhC{b[Ͼ:6#=|PeM_uԓIlXdbL=12m3N= Jޗ/po@g3''_~ɂ]sgvtn֑q(WV䕩K{}zL8ɗ:dbɥ- 6b 1)w<#N?z{qL{)ʯ_ u9e[fU}֝doF ҉\bω/!C4 ULC{`v t?n:%opk.vUbLԁsK:y8w+̰~wV{%~N|A\:2W+p61|bl(psׯ| @Iߗ^^vQ/1 4E݉Bݒ\bHy7`3lIlSg.]AGIudƦ^K>}xMiNGԋ2upo]x$ay$^9i@g3'#/1<U)vDG\+6.]es;a8E'c+[vG#Nb|ȕaF?wz>w~f'9:la_\zxqbyoX@N%9ڳ~y:6Cޛ1Gb|0gCiO,ۇ#PVN9N\ Lv;_#OCu:א>i)V\W];%:29K![LM=/ W:[Q7vouڏYGz)k |b@]~ 8}sh/#u@g2'#.v>]nmdqJ̻O;pٶ6\茕x?3G֒vZO5~izSΰ1k3ѹ#gxQ=<{w|08AysOp P79lZIozCcGrơ튍n>c=q(rĩqb으;#`ccv@ۜ`)z&g>L=8G A: Nugo.Rz9:w9VģX=1ܭr6*oQ txw?6vN 7|b8yx)+'_[e#Ĝat~o?<~&W6J䚴Czr X=u&;~?] t }8 ×7߽+;_ϺQg7N]1,.&o?/OE{ĵO&>>[Oxf˵bgzw{8wC8t먥s/rp;N7Z,N;&~'F7 R}(rT?j?RXۙ+Ly}럶6';~.Ô$q=I \vpݣ_\\u箂t`;\& 6;[I~piTTŏ3-k&:#Ŗ|ӧ>O (ghX2n1boP*;H~N^8y&퀝&WmYܞcQamSڷ>Eizg)fr;G=00},t#4-iKi[蝁eOo0$ǁn#0L].wsyX]Ν"ഡnI }rK^~&ylC&>1~yBa}~44Zȝfk&:?{;PRbLO_O 3[FL>zա!ygFX9ùnON]g0^ʏ|G>\Уm3/n^݌b+;z&NdQEA;q]֑ɍ͡<>pz^+2qc$ qba_-v:o77ȽwG=%X)`[3!Pxpgv'N\OAg 8ybP^8ˌfp\ôWyO;?~]{?L¼mx^tϻO, Wr2@U:]uQK}5%Ĵx-#n6lĩ9G _aW}Y9_Dgy3&qV ݻ?bl~?A]v#yN!a.xoO>mG|hNѢLȄ;.pbˈStbq3'zbc(ɛH&2cy^u:'̀ƞF^7{B߄3g\=w"wX)U/tWpǮ{kOwzաk[G8y%'cxbɳǧ]WQ2p:3Si E)Jg]ډQPO`LHwWw}=Pz2#.쮹x?\5}m-,>7,8؅?ZI?Kz 1m׉ ]G˼ Ժ_`׺K3"v:2c#h@_ 썐؝2Oԉ؂uٺk@ߵa~^}OZjYۼ;Vn`?;@4[ '$(,])m^~䱞=@Vä#%|,%#Sߞ@/gz~x4 MNxؒ>%l)G@[n^$[l__kOZG8BWbˮdj2p'}-pC ;-Y>\${GۻǝeN'}^N }{{ O 9-\ `kW7Gp/GNs_^] +}8WIa ஗5`. |#}Nz\k켍6~8f6;Vg20yX~Aps9{#@~8OivDf'}YwBO x\}^ -޷H%ǵٲHbs8n6Z?mfNy A>)-'[:%Ofeg3p'#vR,n)ZN3sP\]"1[nK@{?\߽ %Wp=FO f|rc=9)aR?%ӽ@%' u7ooL apASR8%no'ME7Kk%Y=?^+.mD?3fOgLzSNx)a=L`&B[$zaN# Fw3p ?i{N4ާCp F,-@@o~ؔ6 fpwB n '3Ѳ3p᠟>q?;ݏf3ЏGLIC#I;>\pqn^nx][ s/םQ`:w- }^–}f3 t:@g3 t:@g3 'YBIENDB`ic11 PNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o4IDATX ͖k\UO~7I4MJQ֝_ qBAqUARRЍ•ƍ]U!46HҘLf~{$u|s3l^ w=*+@ˮغ6ʞ8flئUِkgݵ}Um aŶcἝl0)c₝̶5mV ж?st )F)RO[b % jJlX^Al1;!T"tJʩesC9=zJa.d0p'=!ȬMK!)P hWhD#WNKcwaԒp]9@ k7hOl!ga XmPE+ 9"nV\@dX؍d`)^>N2$3u_s0AK0LNб5TA_*K,ǚ JUF 4#1<:}YtRHrZtƐ5hE B$}äRa!_"Rʕzʹ,W:9o}G?@5rm5R;9J`+m)^wCqN  @FPJ5MJS'%MU؍OA;o=Wq mwG 8[`csrL7hێpV˺m}!8.!K8!@@) V|϶JSPgSiAN֍7+!vKɣcy ڷJ hū+b<ߌGСf?Z R-lue}٣bO7 ^CX P>ߑ7J8ͧ}gy{R!R|%{%Ia_ ]gn`4Ϸ͖}mg} Αd ./9?U3Ѩl"hDy d+b/O>;W.k Ӊ% ۏ6٧#w Ә_ėyDJNIuG>[+'׭%T!g۪Yu@)KtLi Q>i}X[|evѦ2( 1̴BMڵߺxϘ8 c32|=8@)Yq̎-?܍sӾ:290=5Wm#vHq Grޛzj_v&B? ®0IENDB`ic12 PNG  IHDR@@iq iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o IDATxYdUwwC4!("}2qH4> >hH0*11q *j">  2+*Cah@߹ZS}DJ]kpNUu.le`FoF<_~bD~vk+OIWWxB#ܖ>}4}6B>%։edB( !#@T͔UOö sp \, 4D<2K"$`{t $|tQFh!+^#E%X@@]}Y(/I8'I7CV pT]N݀3zg4˴[" *LxUd6 VKG+pjMz${o9 - 0yDYy2%hVe!@ xF4|dR@@/H=Qvâ#.K"aj6ט."Q=cB5Ʋ(ljK>3gVKAfґq鲩^5xhz3oҀ<;@_띠eGkkB%"\7)6R4d`FV#˧1 Q%c3}Iʉ5[krQSxo{I^%SVIXc8 _TP/@#:'sSݽ u%co38|vEMqCY[Wڙ'mjZ Q4yV}댣luZ<} ď?')fJ{cwM&]A-W߶4{$sXONkw%5xvAmW;:۟Ib>V"D"X-}kx5]@s γwp"UQTk;kҷmuպ;g^yO+\uPS[Wk>sy,Wkl<$؝!+N5 nHOQbIDwtC [>)5I|17}ͱBgIkDbꡛIclJ*ec(,X^%oWk'kgl۶Zg2}A傍N^saAXw?6eM!>?$nȸ9:! bz5Y7&:cVx|}+j[_<l״DL;M8|c/~ٮ`GOȂ#4 9_c nkv8!=a~Yjh^} x 2D9{kUi]e̗!7c~'%kiï1@ȦO_q~7PoַgNJc[Ҁesr:Wխ0QT/p+xC9O@̇m:x *0V '0bhc\X6%UF䗀 9`#!G.(|NѺ-P {@ɉ2wϤ>h!eW\}s냄ԗ9 3ou$8 Q Xo1>xH+e Lv(?m.#_ I " ABlU%wKY}>Ouv޲Nok/1\=ʈ+^Ǒ> WϿ% n뷷hf7}DQ@b}胸 x%u[?՝qt@*/uV.v~V W>nZ{O< Osv@KHI ~^#yY޹vS?OVY$S\׬z=]lkW˼ u<#(:6U۶b]N gKOZ4ZX ؞/G%0}z)Lʫ1YC/BʩgGD!ȨG^s֥yPCBVp MJ/ JbgR)vv릥D!(sqlNU*~aMC 2 12B~;?J8+e>E5,x$[AxQ(B?'YXp8ɫRw[(1<-ڸswfLwYG!(,o4e4K KQvuQVQ>c$Fy9;9N9ԝ^KyL!? C8'@!BP|V(;QV "eJN (N="ZBfM!I'bn" ^ZF (;30 7lAi6ѥItwGEPw [1326>п ?m=IENDB`ic139PNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o5IDATxYmZ7"D0&^;0cs# 1AM4 /sri#x#?0I߉ZOջ=\sUj5Hj}xf99ku t:@g3 t:@g3 t:@g639qY/'./}{u:;w:o+_89|rvI;ךy,Gsx݅φ~V_5]S5 ).G 0/} H{'i`Woٵ  |b)k[ϻ>oLw` /&3Rw ;gNt'rqIM獇%[0?5cBJ񧟛{ ԟO? :Ȁ ]ktyvӧ76uwwI+>L<ؿN<")ߙm.I./3x\'Of"73, w{'Ν"wzO5L[Prǯutꑹ'/w};KnF|˼߅PNv$ybuzkz'5%ut#Y,b.?^#qß.7|}i,oҽ qO6Nvᑉ9SIf9Ěű96F=˳ӏ*Odq.aog >x'uf?ɾ}M'=2' PX s0~ üf})'/O߲lz~qpGrѯboݙ̏;ĶK y$_\ oӀpa \"E>ட{pܽiÍ]O~xM]&~nLz/-8OKw>_*Ջ.u,K\b~؏t#c *F:ap /ObYrԓۇGz8&3b';R' \~z/?{9±uO:eEl >6tm'zomb'+z깻#[8o ԓ+:2z侢h'W^\u@͓}? X,?9%}3Stޅˏ9L^qJ'5|rCh́EPK}69f Cԓ#6EϞH\c}].t:1O^iOm`p4\w]<J9vwmS~MqW' ۺ#G;\# %!_%ǁK A.=e 3^-HiN߲ln)K}bsoOѶֵyt&ùKW=zq!ڮo;׋}Pi?ެ{\6^ȭ,y[ 9SS~9ysRd'n匁sCxS܂i2{:tbDO\'}+Lt9<бPX~=>T -wgോ /OⰓ>}D+>R\E ?!R\>l''],'wwG@> ~>gy:\.bJp< NV5Ё;̀{I6>opC:ygWl ];6:8mi '8m'kmb|b@ v'Oj.1gmġT ] }Smv9ai|Ϥ-uW9ofk Y9>[1rS{?c:k|cP#s{i֪\ OYLb}."ݪ٫Ձ:@g3p}ov~_w}OWL+ʝ<|bʥmڈS&N>pQcޝ7glO6b&.t@g3'_Xvw~cN^Hʝw)Nۊu$qg$#~bsmmR3[䑎6<%ĩ @_%| v0*O*7<2ybTN[W?r}Nms=;gr1`,,w& v]UxUv t3n:gkHޟio^?>+?@ʃ_X]sq3?Xxj/ɭat[Iz玮רڪ^_bQxo~rb'IYώcxgObfI]1~@py G6[lO2c mQWl<'91m?#%y;9Ŵe{H~>>cma- N>}w t,}8%5{7ѹם~)f'#sgqg KY dI;ywE/]bwo^C;W("OL8_~s2A6rj3~cg{ OkhȼHq&N}bm\B>%?e$Eا>I @;ӎ2><2cxop̱JX1;Ng3p%p^f7@r]SìʮJvwpZc%6ι;¦Lm0蓧+rkQg:Hʚ凟z~b'1rgvB m"㇞J4ɓZ囂v}u;kD;~h"2cKI5X>[W|UV۬;9.붱Eg\}w;&y$MjӞ l?p$i8i@gF2u#s]YQ]UY]SGbTFS7i#Vb_qgLtkz+jk#ў:%YxH=8}OL]6ۆ^C/asbf]rF&?{\;k赙ظ(Rl tn$}8p M+7;GJW]d#lЍk|gUGUmᬫ>916pbZR_Wb'z2mx "< x PpoS #u\uӯb*g^=r_nG\Dxn;u䴫|K%z1;ھ2qHɎ$۶ tn,.7v/?^y`v%E&vO'K=,Fn#Xշg<8vI9wLxZGκz,od[ڧ-mW}x>Կy4a`Iq0 JWi^?t媞:j_jXj.O[T9s%8mİ_k(']-yoְ`/mcŤUf'|E WZ++)܌bЍlӯb[cKIGf0>ĵ:XUgi '6~Kjo}~p{)&I½z=1o5<Dg/&E;7bmr)+UWmk_hޢmrX"k!V/qƟiS1>Y2FbmᑭRwf >^3&^@gN3FeOMI vWl uJ} ~ƶm=S?i&l`QmfxևC}q?8#i@g3v˫OsO++mr[1٢#3L3ljInIH١swML8Y6KU1HceAz_#VzXN ɯ݁H>~H1˥>q^-zJJcֺQ_)G~Ԗ.Ɖ/g]N1ӏlI}Fzuȼﰭ wV'Ag3p?XypŬo3LSĵnwSqic}1F8cp Wʥ>16YF3^bK~&g~suYfޯ$POXx~)сLj{_3 }Su`'8vfHk6c'1O^n$Ӗ`d :7 pzX\NITZw=z̰3zKb*WkLXiIƥnOoUzZluHO_ 2}Gϳ($A drɋ MO?M>qƃ_+i[:-Fr=iS1vG:^az}7}viC罪]@g2pugߗԓG&^uY'4Hjf\껦{mƬ;um֌O}Q7xɉG[X}G?^D>CoId;0U&kQ}6k[u_}zbc?GpmMnXS~闲7mFx~&@g2'2l}?-n#5]OL1M?6On*gS}oicRɘpa_&v}zg2'2خE:Zٷr[ Vep3Y:񾶱bc<%>#eMo?3{4~0l]ڊ"ߓk>. XZY߶l FU7z/)GqgꍻGj-}f띁;@? !+u&fK=X[Wym:8V;ӖzUV+jŕnkoۏ!g}ZtdbeHߏdKgN3pdUs;ѕ⣟2*Wi Va}rŗC7ӧ xdglg<~?zRON4>i=5O?̶Yv7 P)wg-vdGmgv)?3nW~O5KG׌M?.;@VUSbV{%8uk?}>Q7'ecdc\uǧ>L^H1g,u'?>rֳ Y;/G?SX:vp+6uAyhZW#h `e]=s_1߬2n:'$4ƞ鉝6mwX~x('~˺p(U#5oleW'w(}XlWMdRi5vɭP?A;;>3N[k^mSt[M''Vҗ>>)yx2}*ζz/LxENxXRxڨե?x q\b%vଧXk5U5?T]J첽ԉL a|x)uV_u8h2]-6~g{$'&9xz#Y?XH_Z7nͰUO?L >jICN fr3j[k}G^Y\;[dzbeZGb7NY.eulSG/wH?c\|8#Q1pRжk_c^N}uԖzG|r\ӏtɉg=۪j_rO&Orb& ',_~d4 [0qV\WĮHq˧+m&tc؇:m:Mgܪ&x~WκR?Jy]&&7jr&/+%cr0NRc:mUnz|k,!)bMXG&?_yÎ/)pڋyPȖ{̀'W^;xe'p{T+u+Uz'/3% }$|i~WO&Kկ٦~疣Nz~{#-eg3pM1|RĮ=Fr3[K>7W[3Xd岮uiK}쳝UoY[O[uUlӯ3B^D{ 0NCSw 码DI7Balkqr>i>u}6Y3Saj&OƭݗnV,pԮ#Gi3pG`E 寿[WeW\Җ2m;cƳxWR}:>K&~.}a5۴Qs,>qo$=2hwc%8HN9CcUӂ^{9d%??3*O762}H.qg<6R!#y?Amo|fOW/+1/])]uS_wdw%6]ʝ6HlqTW>)5O>N~ mڌ .c%}'< |.ȏIG:#NX!S''V]bJuW=LOG*Տ|zb3lgq(ɝ 0M̆ Å|Vun=GxxW_M~y |ѣxZRS~'%lX{ڱ'O.V$^IP7S7OHmKOH bbs3g{>1EoMaҕ_$$A ILT\el?GG>bkCy#FXCuiOc1F뷥W:S,<fߺ@g2'C/} ,?U9WqWplF?ڔN`G]=u1z)꡸3+g_cP7:R8[G&67pڦ }DiH1v5[_{֟]eOw p@] Mw'D&vG;OnQ?rw϶刕XYyg?W>7NbSd?2N^^b̯;H.\N-8F7 wa+NLAVOA7!SoknVlbs='`~Om3F/m?}8tg=j@g2'Gw/2KwB'GWvKix>=2a_%:y娋S=D~#LLlͩ?197R?yboCL(y!2H3Ndbo@'pihc=QhC{b[Ͼ:6#=|PeM_uԓIlXdbL=12m3N= Jޗ/po@g3''_~ɂ]sgvtn֑q(WV䕩K{}zL8ɗ:dbɥ- 6b 1)w<#N?z{qL{)ʯ_ u9e[fU}֝doF ҉\bω/!C4 ULC{`v t?n:%opk.vUbLԁsK:y8w+̰~wV{%~N|A\:2W+p61|bl(psׯ| @Iߗ^^vQ/1 4E݉Bݒ\bHy7`3lIlSg.]AGIudƦ^K>}xMiNGԋ2upo]x$ay$^9i@g3'#/1<U)vDG\+6.]es;a8E'c+[vG#Nb|ȕaF?wz>w~f'9:la_\zxqbyoX@N%9ڳ~y:6Cޛ1Gb|0gCiO,ۇ#PVN9N\ Lv;_#OCu:א>i)V\W];%:29K![LM=/ W:[Q7vouڏYGz)k |b@]~ 8}sh/#u@g2'#.v>]nmdqJ̻O;pٶ6\茕x?3G֒vZO5~izSΰ1k3ѹ#gxQ=<{w|08AysOp P79lZIozCcGrơ튍n>c=q(rĩqb으;#`ccv@ۜ`)z&g>L=8G A: Nugo.Rz9:w9VģX=1ܭr6*oQ txw?6vN 7|b8yx)+'_[e#Ĝat~o?<~&W6J䚴Czr X=u&;~?] t }8 ×7߽+;_ϺQg7N]1,.&o?/OE{ĵO&>>[Oxf˵bgzw{8wC8t먥s/rp;N7Z,N;&~'F7 R}(rT?j?RXۙ+Ly}럶6';~.Ô$q=I \vpݣ_\\u箂t`;\& 6;[I~piTTŏ3-k&:#Ŗ|ӧ>O (ghX2n1boP*;H~N^8y&퀝&WmYܞcQamSڷ>Eizg)fr;G=00},t#4-iKi[蝁eOo0$ǁn#0L].wsyX]Ν"ഡnI }rK^~&ylC&>1~yBa}~44Zȝfk&:?{;PRbLO_O 3[FL>zա!ygFX9ùnON]g0^ʏ|G>\Уm3/n^݌b+;z&NdQEA;q]֑ɍ͡<>pz^+2qc$ qba_-v:o77ȽwG=%X)`[3!Pxpgv'N\OAg 8ybP^8ˌfp\ôWyO;?~]{?L¼mx^tϻO, Wr2@U:]uQK}5%Ĵx-#n6lĩ9G _aW}Y9_Dgy3&qV ݻ?bl~?A]v#yN!a.xoO>mG|hNѢLȄ;.pbˈStbq3'zbc(ɛH&2cy^u:'̀ƞF^7{B߄3g\=w"wX)U/tWpǮ{kOwzաk[G8y%'cxbɳǧ]WQ2p:3Si E)Jg]ډQPO`LHwWw}=Pz2#.쮹x?\5}m-,>7,8؅?ZI?Kz 1m׉ ]G˼ Ժ_`׺K3"v:2c#h@_ 썐؝2Oԉ؂uٺk@ߵa~^}OZjYۼ;Vn`?;@4[ '$(,])m^~䱞=@Vä#%|,%#Sߞ@/gz~x4 MNxؒ>%l)G@[n^$[l__kOZG8BWbˮdj2p'}-pC ;-Y>\${GۻǝeN'}^N }{{ O 9-\ `kW7Gp/GNs_^] +}8WIa ஗5`. |#}Nz\k켍6~8f6;Vg20yX~Aps9{#@~8OivDf'}YwBO x\}^ -޷H%ǵٲHbs8n6Z?mfNy A>)-'[:%Ofeg3p'#vR,n)ZN3sP\]"1[nK@{?\߽ %Wp=FO f|rc=9)aR?%ӽ@%' u7ooL apASR8%no'ME7Kk%Y=?^+.mD?3fOgLzSNx)a=L`&B[$zaN# Fw3p ?i{N4ާCp F,-@@o~ؔ6 fpwB n '3Ѳ3p᠟>q?;ݏf3ЏGLIC#I;>\pqn^nx][ s/םQ`:w- }^–}f3 t:@g3 t:@g3 'YBIENDB`ic14WPNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx_mUBi T c⥯#1 h#0 ;Ϟyhs_7/Kg E}H@$VCkb H@~ تzuo+ *)u =he7%^,Y.clӦ l65ͭ5UMH@$pg>X^$ptݣNjm  Eyl9+cK9^f=,?x7QyEǭ p'4Gdj%  H@8vÜ$  H` -@90?g{?++$ZvԫĄl7_3揘l4s}6H\K$  <&j H@xp:_n4EG-F?wQdCk1լqafzNK46m/GI@$pW蔀$  a^[;vi<-fQ;ҌOc{K_g1ڣ>_\>^[ܞNoӗ$  B[# H@88n<@*uk #s4QZkufWKԭmzv:TZԳ*mdﺼe<$  H@|%^$p츝`9#Kp Eּ$ `7m\cjnTl3WM*6?8sGc^vPOyT+Q_SC?qp{|w&V$  H`3>lB$ `?™H@6#omV@:!4ߧUɧ}燄״~Dl⧦bG=}L?y*1M6'~=zڌ6iW41c~/DhE/UǼ$  숀;NE$vͶ:nv#ۗ>.0[1_g ?'1!B;Kf mlKsR}˼/|E5?[I{ڌvxm Ks =67$  QNS$$vӖ̻\׻1n dl?/#V?+]1/?8hgy7rf[q4]հ=fclӦ~M:n0W^?vinS* H@ p%  H@7؆?/[nh)=k_S`.BKQzVꑀ$  H4|8ͭt!$ :vQU[wOlQ/Y~>O{Tfvj|2ȹMGϽRW' H@>A! H@xiܳOty}elӎ[3;1~(ЮhFmؑk̇K1b⟣Y۟Vzl|&^/2lS<]$  H@om&z$  H@'o $  M:~->a BS7w$ϟ N XkS+&q=9_Xc%jdQ1W?jgzL?/:(_nTG^<\$  H@ p^ H@ ;_w};z@Zl%'nig1ojfvb\Q{NsuNMŎUtsj0?LlW4G=^Ӯl|6>Mo&9C/K@$P'@J H@i ʧY y4e?7;ooO;|sd6OG=^b6<M]f'j:vP|8ϝBȆ;mk`~:cks˙hsNM`g׫- H@I p$  H@Sٜ9&C^-~~ß̎Ox6)3MԦ6cFJ]vP|qTm1K#Sz?ƌY,̃1qy=&_~/q1[_v/v;a'& H@|>gh H@y;a $kgl>q3gLUl 3}ڌtY\qݎ-9=㚖md֠Z\G}N^g6[Yͬsy12hgsۏ=uz9oqy,Ԍ$ `suG௡mv;_贩y]|ʦgm슞6XQ;jRjsl!c׮46͍69Y~M\LS_ԫF5퀨= {H@$|x%  H@=z"^K@-`.h0&}? ve(QO֙}ΨWѭڜK#W;F7C=6LY^p?mε+6uB=1cF5"6F?s~lw"@0_TVsM0$  H@#3$  NՋY@On/lh>Gl,i:&:QC;g~FLGŎ\C73v-͖2eNl%hضg.ΉaLhXsqligsg?"7ksO5%  H@ؘ$  H`w6`5mpَ[ʎ'f0بǘLGEiK:ӎhZf*f{~5P_̛|3y>*X+kgs4[*6J;l˶>]O\♚c\M( H@> $  H@`'uB&@F#00?\l;Ҏڼf Gț\3 9yП#gO:hsNl!SsF^9͙/__;nTSuڜmΛ-?XϼƭP] T, H@q p{%  H@7p ft!]7~"?_|Ze n͜vׅ6L_ύ4Ԏv0oř~:e1sYdؼ>4Sc-Girf,ojd%kgskL[7uFc;g^mWtKi{P;jGm~ff.=ؽKlzsZ< %  H@'y_$  H`U}ub&κ- ^fG>α^юv?ǎ|s3kp,W5sXisNaO5-5i\P9qS5>3jg-C6 --*HCm>ҦvECTmꖲX;zms75?5aSO[j0?ovt7mk32zLel#Tt\R?o$  H@$oS$  %\?0oKO+>yF5mЦ65yzݴgv)=G?mjhSS;f=梟65agc+s㟪Y>rd:Y޾%1u]͟2?kfCO&?Kf3'?gBQ8/lKEH@$p.>~ H@@[%L"AJM5mЦf2\e?Tt 4׮ԣMMX㟺^*\9c u,<2uMc_ M%h3?isvfڜTK@$p>N9O H@ء]0?_TYiCf<튆̎#Aͨ=sjldqSl,FEC}K=Qojg)Mw2O&5Ϭ:?4sf[⍑$  H|8Ms$ ء&mƧMڙ&pRW+y4Sc-5g+T Ү䥆smͼӦ&l,3~^2W@?b2 M۟_:<ǹpLSG|7n=X}em?-螜&k?)V  H@9p*$  H@C¥?fEMPX?gξ^%W?R52?b/y]Wydy+LY;d~b3o%~Je~~^f۟6\{ kc^K@$pR>ƺ, H@86I`N۟-0>Ҏl@Ɔיŏz;AةzXl|69^˓f>G}לfL1S6 wm듀$  H|8MuI$ !%  H@'$wNxSZR;|ڼyS?eWrQ3ktyGmb,aOک̝<+{{Nh,1lM5[=ΜT堞6?S/~vbV׳$  H@''o˓$  \#p-][ϊ+>N꿀AL]ŧ͊&363ӻh-gzv6Ff355nj\3ksҏWq`sG,kTr-5墟s\-lΣZ/kU]gjy$  H|8 vy$ kpm $?*3>O-,~ڬAf-5y֦>9^*TJ梿3Z\Wjd+Lcj{bv-&;s8藀$  Hd|8 u9$ *Լ!?_/c, H@x >'ے52MQ=coQ{n~׮Yo*vjl*g?<N$  H@'&oK$  dC}EY-߂6H5GR֗dzelE%jm7\}gNؘGq} 5GfYloJJ$  HD|8t)$ *ԽE-~+hБYzhz]m1u-&㣹Z8Omҵ\7_L?üjPOzg>ul9}m΋y3-cJL#Xfdyz?yة):I@NJX% H@"p-]=?7fs{\+ԷG-Zj~꘧>V1zz未uVg{46r͉%~{֔igb^SgЖ$  <FL H@ ` F:X#\dg9v_ckyUVb{hQ=h*'E?Q sm/kϙ/}Fkgzh^;=c$  H@'oӗ$  B[# H@88px7UzK[b֚K%oE5|O3< yg.ytm/}^f|%zƺ, H@86I̞L 痕h)?fvsl9'{+3~T\k%>G?s6kpl*;o9n=6m棟65asvkKin0[,iW^S8wY :v $ "l* H@hh$<`bggcV^aڣ1cos1FEԍ`mYNjz{4>W&3+OkuҿݯͧzzOQ_?S$  '.I$"xJNw2yo1%֣yMb͜gr.Ϙ&vcFLG mMMzm{j}kn:gqzB_$  d( H@ t h'_׌3b+7)+DbQefFE]_rsr1OnhX/4kJ5c9^J ٣>O%59/+v_1s2zͱ7ig}v"$  H|8MuI$ t i;l7e6i~V޹jxM;kMSr9SszA?V4Znꛯ?gs ]ŌdY>vc{]h6νY;KMf?r2cM=m/SǮ]FE$  H|8 vy$ kFE_RyBQ;g\yi|s[jR*ycbsNQ1+/DpMddK@NL\& H@$(͜UR2}h1+d֛7Y楆~Ylhh9zSG}y*iX#FJ|v?Wu׮}J 5[f>z߷k]5\ŢS$pn>: H@Un\Ţ3#6Vf{+<1,&6˛5O\1նC?Xke1ԭkϜ+s:^3~ =_W41hsg-,WjTLY%3Oԣc4lfv7urJ$  H@'"nK$  T *ױ]E-a6Yo 62k̹f̞S9cY.M?6[ÎZ̻vkg6&31E?sm~QS/pMv&8( H@8'y_]$  H`;_+~9+DS>&o?{?NU+dyUcKS-6{af'#ǘs2_ѭq^G?A _;5kFm)Ry<ԛyyD,uͶ?=z%^$p>& H@(mN] d-J۟Y;,ǶR6M9Vͼ79_֠ }\='vO&Lۯi*jJ.*v_;gmg۟vy%  H@8(z㜶$  H`3V$pP~7no,&a Vّ0۳YfIyr̕٬ __ O룿Y1RC/UyrRyU9}9*[ڱ5x=wvjoʓ?7+$  H|8}t$ !n R)U7'lemHB]fWte{6c7 }iP\aΰ1|zzjskPٌg=3MglY|vk#9sDb͹;3G?wM;jS\&-ދv07$  였;9NM$;o`d5&y'~s B2heF:E3m 5Y;YVg]jX6916c2=5̩yy_b,NEiW8?ڡ36uH‘}nPʔ$  셀{C$!;olRKmFwQkaoEط1FmavL״ Me<},+s>ۮ6ޟ95y2X\z37͛yhvk,_U?Fmndmnu\|('T( H@ { %  H@`u&|L?? @GL6[f{6w>&⠞52;b\1;c3?ciGs6Xjz;kTYɕVUl*L<86fF̛ϬQ?"?+oN H@vH$ H@X[k~_d@Z +vԮz;r#8i3+~clSg΃L_Yh=c+ssb#W%>d uKY~MzUpKlNDm΅cWV>nLXyJ$  H`m>M$ p `7LS fk,8:kOQ;jWbzbzYLϜ36lΝsuzTڹOM3G ԇM]?6r=7h||TgF1~Fm^qN3|yGŏρŋP$ }`YH@6%/UQ h"-k\DfCx)Q?s3hugS6uS׬h|3M̋#YΨeLf3~߯f͜QEfobgHy$  H|8 vy$ kFE*+ϡBd2-쉖mߊc%>`qM\GV#Z8sAzY,55? jsb#O?m)y| Vkcz.C\D },% H@|ؚ$  H@; n£L[& 7[pcۍm`>ҏ&5Cklor~Ц6c1 ];b+ȗcV?sMcxxOMĆ\9e?UW4^8 H@f| $  H@!f>le7 |+*\>;wp=__2tl>YӦQDZYXk6F?iݴq5cLv[ڜ?bS^SبGXiG=^Wbͽzڟ"x}Xe^-g7$  H@$.o$  K]u+] ;/OslZaSW&y٪XyDvП m6n3)Sɕignz^13MR6_Iur=O9zUli'}151:+ H@|$  H@{ }ͪ? h:j3ϛ1Ҽvߪd,u~`̟Պcgl H@[xe" ->uSC]槆mN[לogl3g2_G Mg5zxֹf*fV%FhW4ǜx=jmRxM}iS3g±OөI@$$i. H@Ap(5g$m\>Ŏ3m2?5VjhxMk65ͯ׳ꨡ`l6CPG  V3ƧjcY9,v6cxGmz񙞭zG=lSCŎwN~<yw$  H`>l*$ ]p `W>_|;~!'WlOög/"G%XJ7[75lR9eǃϼ sf3{h3W_0g،ؼsx֦6[Ͷ}]iSQo޻$  H@X 4$  HH6x|o\rkl~ڙfj ҍMR99'Ʉ~p\sϼ?KM%6d:Y/i&l~ڌvUŰN mglfQ?Am\lggo%d<} :) H@'L( H@=v ˆ}g_m槦չvf<cGNsY|V[>^;dzS̨65as6ugvEOM{9+6[򙝵^6V?9E?q;WޯMY$ $7eH@F0BK$  H$In-Ͼ;>his6S<Ƙx}V#c2¦c*~rf`ɱQlƆQ?9LC}=ư֘cOa^G,r6uӮh2}ruYbg?Utp ˛ϞOu^:1M7-l1 H@G$  Hnz Aw]>Ҧva{KRv_mSV4G,Lغ6П\[q,W4SSc=blџY3MyC;Gm֠ͼlɏlGJ$`tK@L3ӵ= 퀏b; CfhxM]$`;o&i#&*|g,\rFmv0WL#~j_񧮳xi33 1+yM{3EͶ?1V?O{Tv}d]OI@$0- H@8$v-'- _dșO-Oi8Fm+?pbsM(쨝bL1oqŷ̦ 7IDATܳQ}g1g6%}L7XQY{?_D=fxy˷Fgzչ" H@*$ sXq E߁ڐ kYh뀿_Mk b36dYǘL5ifͱ-d4Sv0oy&~>rTbcG=lӮ+Ǽ{lF_?|xE( H@. p+ H@C`w!"NSNov@LmQ6)ߌ\ga,[#Kc+FDm֠劘vP|yJCV}ܵ}M֣M 5>nfN'jsO L"/\8&cjW* H@xp>< K@c`1 j &ןǖ%=3=gc||g+v_cx9JlfjWf.?_m3/Nؗ|>6 W/ H@x >|Y5oݐ$  gK$0A 8I@Jۈg]l0_wSArSnٌj:o_CYkv65zm%hVEGMfYfGll)ʾ?O_GK H@@3E  H@$n`r H5Mv@u$+XOާ]P_v{EdTmhWy15tKiZvK=mV*Ϙ96__6?kiWshg|/Jd$  H@!q3$  ,F-P "WKvN͖+[Fb4_a64GmЦ~ߎƟ:iD,|w>mviK$y,DﱅRF$ `wI@V L|b֒$?~P$jdd۟oRiSOŎ@vڙ&GJ<5#Q\k>ۛ?/ŖR[jpZcmv6F/""R\*y$  H@9v~$  H` ..;V 3cԩ+hg~~6߰Y>g>{hv%>t`󨟱SvCUli3kWbXmv6ƶPE[%  H@ vr#$  H`K nYZxH6ȶK mi~u9f>S3]i~3Mة1n6ן4=ڒ詉9f]ڧ?jzlGZI$ -. H@v3J@5'5_p=0̶?5E^Gm#ӌ#wú [aaΨ϶?lOO'Gx^EY $  H8|8νr$ XRI@oI;5퀯Ai3;Bgۘ[FͱvdQbX7>cimx^3's+cOuukc'g) H@X4$  H;ƌ$&Xpzl;xeα_Lfc?G=v<]igJlf۟-}sv}]$  H*b) H@87vνRW'!~8[7y?8g۟`+H>j-+km*zPO;v~f_6~٭tNZ/t.K$ 4%  H@B.S$@ܦ_[81F>?PO@1&R]E س}8F_p߿=$  H|8uY$ )nM&^5[cI͹ OmR"3&x%ާ>eCk~^k vb;'xs9;% H@Xk5$  H`nN$ =moA[a?*[ScO9/`=a.K DJ`M+ H@3|w$  H`%쬭T´M&k_Yʅ||{9. H@8!NxS]$  H)nY϶$.z-. !` H@q+$  !s [s=VR[ =5z"^K@LqT$pJ>( H@4!%H/ G {rlMz$ `7)H@&p=Zߠ$JxXe-'U$ STH@j|qR% H@8}`I~`I>|?~?׼Q9$  `b H@9/<: /p+agx{;3R$pX>9q H@#N rzGNp /M;#$  H|=p$ 9#tw9JmGJp {v$ H@ vs+$  H`;nlA+7eZ[ neb- H@޲$ {(nyHxq p;-{G;om H@p'𖕀$  ܓ[o;p+N-+8(_ h.Fb(M$ H@8s$  H`1_X&I0-乓v;a.Ac1$  ,JEqL$p >>9K H@6 Ipѻf'.I?{F;$  H`O|p.$ l39,99r nD-JZfadH@OCW  H@&0gi^n'D> r'Gwvvǜ$  H`>,$ 8Hl'}0מ ,:]vnk@$|x%  H@0>뺝J6n'7y_jPJ$p*>v H@@[5N n' 1 Np`kYf~, H@$  p{ Egnx oS$  M _$C `7e)5gvih_#LpKۿ_{#``owH@6 -! H@;q!o^Z[ s~j;5$  H`|}qV$ U *^oO[<0`h;GcW$h H@p;|%  H@ /.{"0=Ir5"``)摀$  YNU$"i}/턞g$O|W9w H@|a$ #p wϹv|sNA)n$  `j H@)p"G`B#VB#y)~)cw$ ;-) H@7}/U Jp acr ܑ;·$  H^|yJ@H-;·O`B[[ 1~mzv8* H@8%Ny[]$  H`WL;* H`BX.V H@@[uV*% SpxqvI@D3M" H@(p J$ glξЯ;|$  H@`Ȗ$  썀{#G$1 [BkXvf[u]$K>8) H@$  H@$  H@$  H@$  H@$  H@$  H@$  H@$  H@$  H@8- *IENDB`il329993 90 9,ˀƿ99%bƺ99!'ķ99r996ʺ96[®94{ij9-'ĵ9($ŵ9%#ŵ99#99|Ⱥ99`³959Ǻw91vż9-<ús9''Ws99#+5[u99/96Lzf9969983]wz\5097389 1=MSSN?29994$,489983+#$-4899960)!95.&984-$99`]]̀] ] ] !"! ]D !""#""! ]X !"#$#"! e]7 "##$##"!`] !"#$%$#"! ߀] Z ""#$$%$$##" ] ? "##$%$##" ] E ""#$$%$$#" ]E !"#$ %%$$##" ] E !"##$##"! q] M!""##$##"" g] \ !""#"! ]0 !!"! Ԃ]M ! ]+ ]3]'] >l] 2Ƀc]\^g@iZTMHB>;;IcڹvX@;G_}ϰqM;I[ťhH;TsƥcMit32"w{zs|#t&y+z.{ɀƀ |}πmҁtzׅy|هÿ|z܂݅¾|¼}¼u½½$/¾#)w¾"H $9ÿ&~¿$k"'Q|%v3k½SkĿt)|¼/vz!þh)u'{%ý'b{"&d¼&+t Ŀ&8{%<&~' f$&y!½%(!½}$g'þ#o%þ&Jm%ÿ~$)p$Ŀ}"s&|#r$ſz$r'ſy&r#ſw%q%ſw&q#ſv%q~(Ŀu&q}#þs'p{"r&pz"ſr(px!ýn&ox!|h'ov#ÿub(Cu#o(t{ÿh)kw½x'7tÿq*8o|p&]x|*StpU!n|ÿx#dy¾{R'Rqÿ|!l| ÿb*av|"$o} (Yvx@<8953N^RW^]`:894525LKLA692*++ Á t(++*89:15{{xȁ;2398>C5RKXF56689:-, =̗K-,:9 8,--*o$,/:975663ZYW8B;98963%y|-1:9@R ++*297R_049C_c\Z2579c^`+,,696M_^\-,:97R_^^;EB898W^w~~A0;T_^^Ѐ3a^^T^T^^T^^T^U^^_hhl8mkname icons8mkt8mk@mm3d-1.3.15/mm3d.ico000066400000000000000000007670131466047437300140310ustar00rootroot00000000000000% @V (CC`` kK@@ (B00 %;"(( hG  Kb r {| h p  c HC(L``,@@ (B[00J(( +Y d{mCthz@; {Ch(ˎ``3@@h 00h(( (Ks cPNG  IHDR\rf IDATxydUyU]= 0 {F&*K4fQK^_1DQbD}K1nQ$j$Aaaٺ{f?Nӧ֭|={{{.(QD%J(QD%J(QbA2P(8zѽ)Qb1;< xyF/*/%J,*x܌rMq> .~(1  <(P@ekOF~<}*߃*0\vJU[M`J^tsA iVOa9ӂ9GUQnx8 Xb Fu4Tڱm !L⛰gpszbu,X onEpXapP۟ňD`qrظ QzOjSK1+4Mߘn1& B*5cTB@}88!^`;(B oy,>\ 'TexlH0-ߴ&\ְCE8EkpdeZK `-N7Dgnɼv%^0C=(5$0S?$3&B*4?~C`R,l%ԀcB/W`䡼&CJ7tZGk9:®8-fu4MXiǷ› p/7)Bp j6#FY}0On6=GC~%`0WrS+: /YB |ph1bp%7}VWa@ )Mz2vп)¯P"SLނb"e'VO`9SVX <S*<t3| !hߕ"l)lPS^ 4.i0\ K*0yNk8䰦7|۩g ~7?vo^_P5~[p̔r.l@Ǯb$zY8HPeXo M)U#;q``5g6aQ9YPVb'( `;?5yx)춆7֏%X8kͰ&4'2U-WÉUo0]Bt3yI > YopEwh#?G؂.+y RN+Y)am踃фWG|'nx>%p18>2Sya}T_$Ǡ`PA]˚ xBB$88 6ց`d^.}zϭHzioz}h'Moq@;P ߵ{IA&(v/%0-/9[7E'0G>/F>:Z';j.e U 6'֘$U]|s1/8 ",D /vt=kՖA8 ֽQ)V5 nUW_#B׏v2 ]0 aĵC:OM@3( 54 kb_dzj)Ÿ2 Kc!G_ ق\o@, np;ϑc8I)^,kMzG@y_=Um/mʩ.'pN:Rڶ. 9՗v^ j~x D {9nzP>RVI[txS!IsD"(z|$:lXGkv~ t{p$,ԛwU8![َ+` pA~ ϓ6L~x±3Cy\q YҴyns}Ti26 Ϩ?ˆ{p=\^ jH <)o/ v l ;y̜sfvI80Pua&1y1y3% xcM-]a5 )}c?ƴ2%PG΁ƈ/1>` o1 M`?l<o\z<إ] -.A41qGKq.#BpA2mO}gUHZ=vV}˛`j}Á:^_~*^~{)Ϋa^p.|u 1rfY}McѴ~<τQ@bVݜQ}S}` wHw|b @S3۔2Ix͟gAIRp<ps,gnK6cIG^_؇[xu!!L&,5u CSZ(Ա]J0NnvV1)/$O$pIĆ]Q믧 UC {vb)cm6f?Kܿ/ Ę8Cul11F\%[{'C}pF5EX8J CX~ak#oz±!SX7׬i|%`5p=g1^1>5`O࡫KQ@ЏZ返ٛwT2c_!Ź dHp}`urxIkH%! ;.$P5<mvC8l`;+dO]k>X9?FKBo T p,c;T`'x5 ` NkY,ݴ~э D}pw)U1řɹlf 37~?׀C+.o>)~?R>@c[*_F/W X A.ZN`Ǭ;MZ=䓈 FSC*GBYQk?:lb{ 95#)@,`;ZiNr}>m#!1c1ec ҾsM;j*h,O/ 2I%uqz%V_FIkæR}ۚ)GmgN0/hBfyh(`P@sK( `Q0N+47IȰ]>(Bӈv%B׊_`XY>6qN>Nc '!ٗ_pۚ?6QwL." W{p칫M1S*J=BoCFImb ;y"?` a!~C9yC}hp#C ۧ":$ϵOlP]J(m88k~Is^0F&CH ՟-g"ה!f`<-3$ , 9 3ʔ* V0*F?e3Fu KiRߎ9rTg/^EX51f><%ty4p*2RziJar!JY׎8i |%FYI(}8]OYNVpf|Z]K)\nޘ#6]oJ#Cj}A3u/Aj"~ԻDZ)Kcnh )*g#F\)dۅ1(_B`Yu`z~338x;pv^1>: .iÑ*'z#0rQ٦ :] f^`P_:KE7+Q_96[TfG;SqtW;%r?D2͎;+rs jw2XP:&yepĔdžOcBd"p'Ǯ:cwE=y]DIxrEӥ\c}c+F Ť ]OR8I\'Maq̞l>N%u/v{r-.lWKiaNXBI"j{%Y!T+!łHl!1uCل`0,+Mل i ~\c ;bB%ԱCR}v>;,! Aj:B}];>yB39$7~k+܈.SE FוΗ">@s#-z] ^W]fLb\:]!HÀ e4"1L0@8%VHj6!Mi;$$[G.RFGB2 Ta,s}uGg>4P+SЛ1#!`3E Ju\I?fF  P@z| ;|-j;T$'VzEtoN9WY;S&Ej> Nq!:v7-NF-h&D9 98ѬE?0&ntN!W!HtLMoǹ|Wzpi ̣ TQ!$J ,#4OHBUv\}Y6RX1퇬ĽXP)i{mm#R}R;-T.'<9mZ1֑Ϻj:}/̩jJlw ]d"jW.$`!+퐀#9%`U*GJR4m $DRr׮hBm$l!"7EY)!@z gxp8 3!{hHb#]i.hXWR(9Z(ǟ++]}.Cd.c. YU،c4P'sgs;+viT BR?ES]f_A* v9\'-5Y7VHA2;=F rHu¡S_GT1}MϽΩ%YJӧqZ&$(R`cė]EHcԑ B iMsl- H'?T]W ButBcmSB\d;| Wm vs;_hWc)B,͛citJSP!}/_>~_ѩU9h)'(@u 0Ijy5$bawiNlŸT?ltӎyϤ|@@,BX?G}$[|>ሱz\E:C#", @^bd} fx.Rr<9Cy峉4DE++M(, 3FocvnFv i'$Qt1yx>D/B 븍]M$|e˩3E tJطr6B}|1,&Ì1mZ.R 1BR.9mtiIK~OS T`:Cx.O?G%B'8W;\߆TGiD`+s7g2inO()}C}u<IbP4䘾`a#eMj"Rr-UsI4˓^$r fx ^`8jPWպK;֟RW*5pL{\`Nzyj;eMH׭YpOq=js[{ݖFlz\U.>W$鱳"% `?);#elmiLLʅIN"ʴSNmL! /a#)g2XK1ocLy O o\ys(oBF_[.b5~hsFր.gkI)C)]B"s b"E@[6ԺS"Uw'Ցbt&BӤAk=8)cP\9rR;.g`ξ\K $r a. \ \Rx:re첱=G E|7ډ&ݚqBISN=11OSn!@K.Jc}H)Pivx) v;fbr6r-"f&eu@RLJWPvSbj۱.]A=ZDI2|@p) Pʉ]w+v^){>RfCHc w3ˆd#~LR\@SoHI}g!TgP?XQ\|~8oy/t; !݇HWo*rXᎱI˵tbS(x<?+;'>scLGN;s.^!3˅RX v ]@,|!@B>$'^׹Z R7]P>m1D,5귉 &2Hi/O.b +^u9 ;o̽='!%\u_ix2\}7Ο:/eSڑ,Y1m`]եQ]D|ucP^W&Jwaz]kc D 11 Is *2>uwh~j)f[&!17/K]I9NO)R눉gMm7^(bbW9L0QZ=žӦd!4hn;9g]wrINpuhx_ZK>Q@\7 !SQ$o5a;/ \,!Jv^|$)ojv;$IP$R__;!6L:0NG Ho6PS`Jqo+%y2^r tnOн˻ڑҤ |{P啿 $^ IDATv( #`5.l =,1q+:.2!HB#v˹pj%>"w[WW>%pR xYu %6QS"La)؂b)}YkB" nq(=V>C L_ImNf9cN\sB'=RاդBivBھrv4uL^_9qE >W$/Yαa|7" )XtyQsʛfG9;F'Ŕ3]qJςTM@LD V}s\S|!@(k ̗4pbCj+fS@Rt||e]Id|aI$j೉ g"Ƃ"ƿnr?  y#&%hq9hz \V̼i/YC;yyl礖sv됴/՞Ocm{I*p pu$s>gp=8 oojL0}N7&bf]9i䦵SO%v~W^=w3:}fm׋6O Q<^: xU +\ž;$HJv>g"$(>rߤ2RկK~ǜv-K+Ӌz`C_Aߜt,`6a965|U`"լobz׆cIK#G08_JB*ƉP-KPd` م` eVH7^c1k`лzWnW/Pr `_!1SO,49}J=B?6v Db%< T֌FEV^5O/DƗK E_OT-7A1 𷑯ؖmKu3CPqM|$r!APx6po!ԑ")D9Z?M@ J!r~oI^~pOIm#i6}m+n_" W]~yl@jйO]RK`H5+ i^[9֗r,P931$#vr_ކU"S!Ŋ /L xF^1vqo0HJB ?D>K𳵽vII`8)3s^<a\ÜЖFJKvOm#bp?3o&| ,Q( Ss'R-aw. p!g%Iu Ƥ |L5ʹ?3]ߩGBImb bsX 0k8ai Tm-]GZ o;!ΥH}yd!21)фOJh{Yl pl!t0s!Vzu])- #r-M4yn.5܄] ' %}f Sg:Js !(Kia@mAT1y;2旈A>ow8갣֯%6(,egj/5(lf; X B2C x'Ba_3?*(glGw3pI,Y( Tc9;X 9{9=NmRzy#aYPĠZD[&G}+.EM_AUJGԗPDLfE{{X.` uVG" d hkM|3M94(>UQ/H'v.}6 O+(, I/jςg4;fBS9 G 1À!C;kKyc<($% hJy`y2C57ڬ7c59: o 4K2mJk $E+ֹ ] eH ۚ%񟀻'Wn`>Bq2Rװ3AZ3 I |d55l pYa%_`X!)Z?4''>xهר]!tn}9ъ\D!i|Q&KV6J f#UӇH׺"YJ3~zru%t8{UM}Ǐm?@$!$1o']VTTMo)|sLL0Ma͋.wqt,!d2K/$\t+F7=arݎq Gw_! t&%k.J0RTsX¦ @p^}(Ye`N\:/#<3?k׏t%t,e3ԲG5BaڀW N.%KQ"M\vQ@0:nϨ7,/$}C!>2 oclJ?f_J7k( hPksVu:ݥ_r-wye LaA |cH!F}y]?fk} |%p9 CI]..b *u4IcH@#'\i2M+NcPߐ7H1ͰM嗄4[S~uq̒Cc9\&p m\Tƌ̾D`I}w'_sJ?IC~sw 5pv P_ 輦nE}Ny5lc`%ZE~%kp5`~[m~\"\]~JIzO3_R, wk&yE{K[Gv3c/T>p2N_$+!0)OmKg%.K@zb{쟀k& _W( ``7f7`M4i:lkà +6.2L|hך L?t%#l2Safk3t/ l+\r: %  >\&ku~w\^]GI{8 g9e)3H)C[!tJoV5!#i?wŅ6h_ӝ# %c(kT]ŭ,cê IC)Ⱦ5x"}eCco7`lnm_G? %yr*gz4ね1$'I$. #/tIۿ2u8tBրs.h+VS~k~sN:-LQNN I%:~vZF\퐀Os-w `o0Κ[|Gߝ$z&5 :zIG[`%bTnV)M?5DKˇ1f_6"vokz|?kcbt V/C}GHRǒ:x:Xϩ\Akgu H@"-!idžm@;޶9ƷkgOZNvw$ebUwjneDY6B`6 )!MM%%movɃ ~~Nz>Q:{ B)F`!`9րG 4懙+CF8v/GqLwFé~/Jq4p/e/ {YW@/6p9C64"I!^}>i/9oM<&0)VOe$Gq60x㬡ɍ@ k0^?VKBvK۹kP ;'JX` 8wd33@ ˉh ?yV #v^WgC 2 3iz:$KƳ^Xo( `A.A.Yg@?JCMI)&~h`p+/^7N>p%涪2JX48,RJ0}< p ޷p $#lf~~ d5N.v~&i/Ќ>Ġ#H qnv7mnXGc4c>A O X-I$,2ȝu yw! By5Θ-{xr6q&clB5`3R=p3W0ERg"D5H8g 2H!~Nâv?OOsz30 LfgR&9b:9+8PDKww+ ,w}ؙowOcH$>_˘ȼ?Za( jTQtг3w]2r?T Z B޳V0lS_{s܇<0 q'Ǯo@y)qesݍyQzEDM7mbM)+"sZqv?|5&[9xڵp+˟. |Л5嚄Zj% p\A*mu/rv0r-} r# *jz/"D@̘vaiϢ!^]X~_+73Gջ )UB9 ; 8NA8ue`3H>L=81`8g--]@4RO*Uꐒho42b)0n;n~u){av9ۓu~9[u/%Ag &%kn__\$=@[1B}84(܇k [kQ _g1p.o/=@1:|55t~[b~ãPWxT'{ bx ^wJ-/WY C \!> PIؚ5%X$ P_J1Èa݁|婡:b#DD~.pJ/'jHQ=:ߝC"5e%fa+;Qdv;Tp"MYYD>nÇ^A0S_Z?zv%.s/yۈP/Dd ̹e%XCx뽨]|%E=ꍨ{z \)%^{-J3| @3MĥeK Qڹ:Fxa@V&P_(L)m ou; Yh-'{oh'jP xCmq4jlR w'1 o?Ă jlK_Gc]..}}8lW?*,*4`ZԻVYK}1m|8c`l|g Nø., cR}x@IcԿޚDb6gY椄8y!y)&YC#PÍhp Ja#P8jZeW!TCT (g#= O0|x; Fz~@; ~t^#J :wA%,e,~t % j{/Cėy*@mV.j ~W k%,ϑx (c ? 7UO$8>|Ñd_Z #E3w( `Q6 |ʝ>O%U~澡F/2( [C \Xx|Wz}1jJ'8]B#q%zPqP/| /6J(AQS1}o09LQ@x PKԍp$*BȰx7s:]nR{ۀ%!IDATg }_, #۫PZ%#Wv$xr绳pQ@y>ԧ{%È#nA v A/ug@:77aظ(b(}%(ˁ_ *QD%J(QD%J3?8,IENDB`( K^Ah^ABB7h^-M^_^JU^v T^^1^ErrrgT^^RT^^^T^^^^4III?3a^^^"^Tw~~A0000;T_^^^3;EEEB88889999998W^^^P%%\---,:999999999999997R_^^^yyp`+,,6999999999999999999996M_^^ c\\\Z2555799999999999999999999999999c^^  0444999999999999999999999999999999999999C_^ ^=PPB ++*299999999999999999999999999999999999999997R_[yyy|-111:999999999999999999999999999999999999999999999@R8BBB;999899999999999999999999999999999999999999999999996333%$,,,/:9999999999999999999999999999999999999999999975663ZYYYWK---,:999999999999999999999999999999999999999999998,--*oKXXXF566899999999999999999999999999999999999999999999:-,,,=^;22239999999999999999999999999999999999999999999998888>CCC5Rt(++*89999999999999999999999999999999999999999999:11115{{xO5LKLA666699999999999999999999999999999999999999992*++ W^^^]]]`:888899999999999999999999999999999945552W^^^^^^^`aaa_>@@@<88889999999999995553N^^^R^W^^^^^^^^^^^^```aVLMMN777699:,,,,95=W^^^^^^^^^^^^^^^^^____YYY[53 @W^^^^^^^^^^^^^^^^^^^^^^^^^^=4NX^bghijigeca_ZT#n^^^^^^^^^^^^^^^^^^^^^^^^^]C;Z_dfhloqqrsstttqpnlkllihx1 ;]^^^^^^^^^^^^^^^^^^^^^^^^^TC?\dhlquy{|}~~}zwtpmlhKD7;vB]^^^^^^^^^^^^^^^^^^^^^^^^^V/7R]djqw}}zvtph^&KM*B]^^^^^^^^^^^^^^^^^^^^^^^^^V Xbhnty}ztlm$ I+B]^^^^^^^^^^^^^^^^^^^^^^^^^Vo&Qdkrw|vrjUg5+F]^^^^^^^^^^^^^^^^^^^^^^^^^V8Ibntx|yqjVBA+^^^^^^^^^^^^^^^^^^^^^^^^^^^L"<Ymuz}wngq4)Uh^^^^^^^^^^^^^^^^^^^^^^^^^^SA:aqx}|smiR`Yh^^^^^^^^^^^^^^^^^^^^^^^^^^S)GgrzypcUt\h^^^^^^^^^^^^^^^^^^^^^^^^^^S_,Xjs|~ugZOh^^^^^^^^^^^^^^^^^^^^^^^^^^R(bku~{haEM1T]_^^^^^^^^^^^^^^^^^^^^^^^^^Yc5flvnYM6U^^^^^^^^^^^^^^^^^^^^^^^^^^\O?dmwt]SZT^^^^^^^^^^^^^^^^^^^^^^^^^^^w1hoys]RT^^^^^^^^^^^^^^^^^^^^^^^^^^^x.kozp[aT^^^^^^^^^^^^^^^^^^^^^^^^^^^imy~k^s^T^^^^^^^^^^^^^^^^^^^^^^^^^^^Wlw{f\JP^^^^^^^^^^^^^^^^^^^^^^^^^^^=BjuwidQX^^^^^^^^^^^^^^^^^^^^^^^^^^Cis~rcT^^^^^^^^^^^^^^^^^^^^^^^^^b\q|pnT^^^^^^^^^^^^^^^^^^^^^^^_[Vlz~nxT^^^^^^^^^^^^^^^^^^^^^^^f<ey|u#T^^^^^^^^^^^^^^^^^^^^^^_AYvxsV^^^^^^^^^^^^^^^^^^^^^^^7$o}{%E [^^^^^^^^^^^^^^^^^^^^^Uav|2<[^^^^^^^^^^^^^^^^^^^^^\l|b[^^^^^^^^^^^^^^^^^^^^cRq|R[^^^^^^^^^^^^^^^^^^^^edy{R[^^^^^^^^^^^^^^^^^^^a8!n|x_!Y^^^^^^^^^^^^^^^^^^^e StpY5m{^^^^^^^^^^^^^^^^^^^b ]x|qBrz^^^^^^^^^^^^^^^^^^a%8o|pirz^^^^^^^^^^^^^^^^^^b,7tqmrz^^^^^^^^^^^^^^^^^^Kkwxgtz^^^^^^^^^^^^^^^^^^Kt{h0r^^^^^^^^^^^^^^^^^_ Cuod[^^^^^^^^^^^^^^^^_ovubZ^^^^^^^^^^^^^^^^_ox|hZ^^^^^^^^^^^^^^^^_pxnZ^^^^^^^^^^^^^^^^_pzrZ^^^^^^^^^^^^^^^^_p{ry\^^^^^^^^^^^^^^^_q}sR^^^^^^^^^^^^^^^_q~uR^^^^^^^^^^^^^^^_qvR^^^^^^^^^^^^^^^_qwR^^^^^^^^^^^^^^^_rwR^^^^^^^^^^^^^^^_ryQ^^^^^^^^^^^^^^^_rzW^^^^^^^^^^^^^^bs|W^^^^^^^^^^^^^b))p}W^^^^^^^^^^^^^eJm~W^^^^^^^^^^^^^^doT^^^^^^^^^^^^^^_gy^^^^^^^^^^^^^^^6(}I^^^^^^^^^^^^^^^Jy~I^^^^^^^^^^^^^^^O fI^^^^^^^^^^^^^^^b+<~gI^^^^^^^^^^^^^^^`)8{~J^^^^^^^^^^^^^^^`0+t{%a^^^^^^^^^^^^^^^h d4e^^^^^^^^^^^^^^^eb{zGe^^^^^^^^^^^^^^^^d{ze^^^^^^^^^^^^^^^^cue^^^^^^^^^^^^^^^^dvzh_^^^^^^^^^^^^^^^^^P|S^^^^^^^^^^^^^^^^WktT^^^^^^^^^^^^^^^^_P kwT^^^^^^^^^^^^^^^^^dQ|vOT^^^^^^^^^^^^^^^^^^fknT^^^^^^^^^^^^^^^^^^]e~rJI_^^^^^^^^^^^^^^^^^^^+97yU^^^^^^^^^^^^^^^^^^^`$HT^^^^^^^^^^^^^^^^^^^`0)wT^^^^^^^^^^^^^^^^^^^^e6/AT^^^^^^^^^^^^^^^^^^^^^`FyT^^^^^^^^^^^^^^^^^^^^^^uq]^^^^^^^^^^^^^^^^^^^^4x8}n[^^^^^^^^^^^^^^^^^^^,^yy-|$[^^^^^^^^^^^^^^^^^^ ^{y z%[^^^^^^^^^^^^^^^^s|w|%[^^^^^^^^^^^^^^^f^zhy}%[^^^^^^^^^^^^^^B^|x$t"]^^^^^^^^^^^^^b}msny^^^^^^^^^^^^~{|b oyw^^^^^^^^^^^|yHSx<yw^^^^^^^^^zm|yw^^^^^^^^^zyYyw^^^^^^^^|tvkj5o^^^^^vu}IwX^^^^s|})Y^^^uq+w{zr3X^whGjBlBoBmFX {*X^l???????????????? ????(`  ^UiW^30&s]M\^m\^]]U\^^=W^^^NQR7777;a^^k3../99999999N^^^!<88$+,9999999999999995Z^^A1;;99999999999999999999999A^^AajjA333999999999999999999999999998Bb2^ "++:9999999999999999999999999999999999U !//499999999999999999999999999999999997776MLL>LLB77799999999999999999999999999999999991//+*/.:999999999999999999999999999999999.++'#++.9999999999999999999999999999999994331mjjH/8889999999999999999999999999999999999999;;;]_]VVW56679999999999999999999999-+++\^^^^^aaa[IIJ6778999999990..,^\^^^^^^^^^^^^```V>>;86RQQ\^^^^^^^^^^^^^^^^^^]rEPU[\]\YWURHj^^^^^^^^^^^^^^^^^^^eR8A@]dhmqsstuvwurpnnkgX? r^^^^^^^^^^^^^^^^^^^[!dfow}}ytooc:9r^^^^^^^^^^^^^^^^^^^[Rbjsz|xoe*Ur^^^^^^^^^^^^^^^^^^^[#Eeov}~tj.M =i_^^^^^^^^^^^^^^^^^^]Fdry~tm$PU^^^^^^^^^^^^^^^^^^^YVlx~{okS(U^^^^^^^^^^^^^^^^^^^Y]n{wjV[\U^^^^^^^^^^^^^^^^^^^YY1dq~~nZtQR^^^^^^^^^^^^^^^^^^^[+fruV;X(^^^^^^^^^^^^^^^^^^^^m4gt{\W0^^^^^^^^^^^^^^^^^^^^M@jvx]0^^^^^^^^^^^^^^^^^^^^hur^j0^^^^^^^^^^^^^^^^^^^^jsm\g1^^^^^^^^^^^^^^^^^^^^hZphR^^^^^^^^^^^^^^^^^^^6#n||jSnS^^^^^^^^^^^^^^^^^_@gzvS^^^^^^^^^^^^^^^^^^TvwK^^^^^^^^^^^^^^^^^T pz^^^^^^^^^^^^^^^^d^{z1^^^^^^^^^^^^^^^^Yk*R^^^^^^^^^^^^^^^^1*vZ^^^^^^^^^^^^^^^cX{z_^^^^^^^^^^^^^^irc_^^^^^^^^^^^^^^ Muu_^^^^^^^^^^^^^^Uzv_^^^^^^^^^^^^^[qdzZ^^^^^^^^^^^^_DulY^^^^^^^^^^^agyrc Y^^^^^^^^^^^ahz{l Y^^^^^^^^^^^ai|s Y^^^^^^^^^^^ai~s j^^^^^^^^^^^aiu ]^^^^^^^^^^^aiv ]^^^^^^^^^^^aix ]^^^^^^^^^^^aiy d}_^^^^^^^^^^ai{ R^^^^^^^^^^ci} S^^^^^^^^^^@g} S^^^^^^^^^^`e K^^^^^^^^^^b\ ^^^^^^^^^^^b}^^^^^^^^^^^\v^^^^^^^^^^^^Xs^^^^^^^^^^^^ Q\^^^^^^^^^^^j{|V^^^^^^^^^^^evxV^^^^^^^^^^^^34V^^^^^^^^^^^^.5yva^^^^^^^^^^^^fy&f]^^^^^^^^^^^_D ]^^^^^^^^^^^^aljj]^^^^^^^^^^^^^gs]^^^^^^^^^^^^^^@i^^^^^^^^^^^^^^b-=_m^^^^^^^^^^^^^^^Mm^^^^^^^^^^^^^^^^_m^^^^^^^^^^^^^^^^cmY^^^^^^^^^^^^^^^4W {'V^^^^^^^^^^^^^^-^z{V^^^^^^^^^^^^^V^^^^^^^^^^^^ys}R^^^^^^^^^^y{l'v+^^^^^^^^zwo8^^^^^^^^|pQx y8^^^^^^^^q ]`8^^^^^^p>}6^^^^^x{R^^ezs0 zR01QwhkolM??????@????(@ ^rX7 *,Zd=EzTp^c{W^ NY^j^ (2U[tn<*2R_^$<Qm{q[SB1356899<X^ ex{:>;;9999999999998K]{&+125999999999999987751[s`L-/339999999999999923-,EihiLMDB977889999999999999431--^h/035599999999999998888;@@HAUm?HF@@?@:7788999998541.C[V}e.e__`ZZVTREG676/*<s{/d^^^^^^^^^^^Zby(,010/,w*h^^^^^^^^^^^]j "2Igpuz|}~}zwtlQCk^^^^^^^^^^^^QW9)Ut|~w0V. /i^^^^^^^^^^^^R B;du{ gLC >T[^^^^^^^^^^^Y\xzhn8MdYY^^^^^^^^^^^X!^|qboTaZ^^^^^^^^^^^Zf5k|`' ZU^^^^^^^^^^^^Y`:oeNU^^^^^^^^^^^^Y*o|egP^^^^^^^^^^^^[`vd&Y^^^^^^^^^^^cOrtY^^^^^^^^^^bB{tyiX^^^^^^^^^`Eoy1T^^^^^^^^^c#<uOS^^^^^^^^^Q ^}l iY^^^^^^^^^GtuK|\^^^^^^^]+9u\^^^^^^^\ `~j\^^^^^^^T i;[^^^^^^a)9pBZ^^^^^^a,7{A[^^^^^^a,7~n^^^^^^a,7l^^^^^^a,7l^^^^^^a,7^^^^^^b$=|^^^^^^cKz^^^^^^`?D]^^^^^^\~qG]^^^^^^]Qx*\^^^^^^^52~[^^^^^^^IziZ^^^^^^^S e'z[^^^^^^^"HunX^^^^^^_^xnpX^^^^^^^_NCV^^^^^^^^a'Cx R^^^^^^^^^ZcCR^^^^^^^^^_@$z\^^^^^^^^^r{sOZ^^^^^^^^X/?{X[^^^^^^^^|yoSX^^^^^^^yysNvW^^^^^}`y[ V^^^N^^~y7W|f^^,z~US~\+^~q3z\YYk{{[Qlj[^w???????(0` ^B<g'@x^v^YbP^^"^66oj4-3598Q_;O-339999999998Z%CC@78899999999999541\{lY:046999999999921+YmrZ*11:999999998552FQrlmO^_XM<:999999;<>`I^^^^__[[XU"$#!)^^^^^^^^^`S>av{|w dq5 ^^^^^^^^^VOs~z]@_^^^^^^^^Z1Ctw)T]`^^^^^^^^Z:L}ma^^^^^^^^]bpZ:b^^^^^^^^^m cq]^^^^^^^^^g,Pqa][^^^^^^^`4%zq<Z[^^^^^^^Ttv^X^^^^^^^9'kW^^^^^^c[~k^^^^^^XhWr^^^^^^):y|^^^^^^ b^^^^^^ ft]^^^^^ i:X^^^^^ l@Y^^^^^ nR^^^^_uU^^^^^ m^^^^^T^^^^^>!V^^^^^g40^^^^^aW[^^^^^;)[^^^^^cYuwZ^^^^^^Z/W^^^^^^^Hm^^^^^^^dRw]q]^^^^^^q~x]^^^^^^x<c}\^^^^ yzHX\^^^~2E>c^}^z|F^^~r} } ???((P ?a{Vzwm^7[aH{k_^;\oKDB@;X^)9z~Q@@><;99977>ZK"]B=<;:999877653cq|cK9:::999998884\{NJF@89999:::>Zz^X^__^ZSKhV^^^^^^\IFkz{m\B3 X^^^^^^W+Sy@S ATDt\^^^^^Yx3scl >[^^^^^Z XCr]nY^^^^^[ydCqStY^^^^^]nZ0|qW^^^^^`Akv/hV^^^^_V#FV^^^^aI miPkg]^^^_:,}>}[^^_T OZ^^`LjiK X^^`J rvb W^^`K t|_V^^`K u_S^^`H{arc]^`K m~SWs\^_V&Ln2Z^^_;.Y^^aG}jW^^_W%OiV^^^`Hva S^^^^_84w Y^^^^_X t^r\^^^^f#wJ}[^^^^ t iY^^xEO W^F^^yy8P }v1,^s;GPo.'^Y{z???( @ =t 3so 5LmvU2^8h[TKA]jcs=567899:;Hs0kG-.01326HRfn&o`C6/3//,2Sh4\]\\WOHG ]^^^^^[ .Uz{@R`6k]^^^^T")\6^[<T[^^^^Vu4xkiX^^^^Y/laW^^^^^l|qQHU^^^_Yk h0R^^^a3-rh#_]^^a[v\^_L x\Z^_A"tlY^_@$}rX^_@%rcV^_;*t2c^_HUj^^[ov^^c"M]^_Q<]^^b36|w]^^^` u'S`^^^^ac )2a^^^5}d^^2^y^]vk^^4wpMy ^t} s??????0x(0  : ^7wx1f~fM^0d,.245;FxZTT9:999884t>hUTQHV (t^^^_0kj. \^^^sb.Z^^^zf`nX^^^^zV^^`#FzZ^^dy 4m^^<&rK ^^.: ^^.;^^$D\^J>Y^e V^_1>oS^^_ |Ih^^^<+*5^^z"^q^yy2}vM<^X wx%?(, jjH ^;I>:[sXJA6741*`[z_@24/,<iAV_`\U,MKLSKRf?"_^^^S/~9\^*FT^^^WNJd=^^^\M<m^^^Q ^^^ Kvd]^a yw`>[^b-Z^be^b^_f&^^8.z ^^bqQ^^^Y|T`^^Zx !a^8^7Gv lsdzs'|<<<<<  |(  :^, >s_SmW;;?Jv߆ ^{iD:^y  OY^lZL'x$zfI}(l)a^i w]!h^\b p^G] w^.D ^)O^,Js^C(v\^V "H^a68^4]^Y3I|[#6K ( J|R <|X]@nPJ]os^U3[NbGlI]Z0~_-[^W"Yc|mXMUHbT~cwa[SgYb;(,3NL#  (  Biɢ~r3pkHNDYq> Yj^w)  deXBH HV Z0JT@!]@@( Bym)e`zRtT"Cpy wr.SP~*69UJ+-( *.%/4:4?9DE@@LKRXYSZ`QaX[b_iypqhNCTOz{rr Js 7!G 8"+*0) 5+1* 7 8-=$8<4.6*8K#<,>M 6<N n*"Db(J?<E:U*VT$GPC@(F>BWb7&LSs0z1N@X^h4KE _`^ZUabWdhigjkl ce lmqstuuvwn}~yz rr__]__r($(.   oRRZ42B2 r  rrA @]]@  rA((((   rrK    `rbSRRR  6rK4AB  2224    ZSRSor`r   7DDD  $_.r    D]]DWr_    ;rrrr_   r_  BF22R`r_RRbRbR rKOr`ZrLVzq[ZbZrR &ohOgO4K~uzXr4KMq|dr4K_OmºWSBr4~Rt¼¼¼¼K4REe¼¼ºxr2Qz¼º rfºwºr{mºr nºF{¼zVrº¹rº¼r}ºSxº7"ºrb¹romºbHļrb"zºR0@ļr77mr7m70ż6q¹rĺr(I¹Mº(-ļr-arr±³³rĹrĹrr¹rĺrļrrr¹r_r_5¹r_Pżr_¸rW¹r$0żr4ļr4!żr4UĹ.Uͼr4<żWźij¹rż¹!³r®}Ĺ ¹³rMĺRaļb?¹b>Ĺrb1¿bĺo¼r7¼7ĺBĹ7ĹBij7ź³³¿¼¼¼¼??@P????????????????_?W(`!*.%/%4:?9DK@LKRXYZ`QaX[b_iMphjT>Er s 7!F|0)-514E G -?IX2 ?#ET7T*MV RC@URS,m5UN@VU,M$P(^Y@ _ XWZb\]dhXijklemqmstuuvwn}~yzeeeeeeeeeeeeeeeeeeeeeeeeeeeeeev+eeeeeeeeeeeeeeeeeeeeeeUeeeeeeeeeeeeeeeeeeeeeeeeeeeueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeueUeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeejeuuuFIeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeueeeejeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeuueej$00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeP99>eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeuuee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeejeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9,*N      eveeeeeeeeeeeeeeeeeeeeeeeuFFu       9eeeeeeeeeeeeeeeeeeeeee   UFFIee߀veeeeeeeeeeeeeeI//$   ,,,teeeeeeeeeeeve889  Feeeeeeeeeeeeeeeveeee      9eeeeeeeeeeeeeeeeeeeeeeeeeeeeeet8     *998eeeeeeeteeeeeeeeeeeeeeeeeeeeeetu   0$$evveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeUFveeeeeeeeeeeteeeeeeeeeeeeeeeeNeveeeUUeeeeeeeeeeeeeeeeeeCEZ`pplpbcZZE>eeeeeeeteeeeeteeeeeeeeeeeeeeeeveaXnk~~wisteeeeeeeeeetUeeeeeeeveeeeeeeK={wh6eeeeeeeeet#ueeeeeeeeteeetYwkSteeeveetteeeeeeeevvv)M~d9>eeeee*eeeeeeee;{feveeeeveeeeeeeR~eeeteeeeeeeeh~neeveeeveeeJkneeeeteUeee+{Veeeee*8eee_{neeeet38eea~ºheeeeee*8ee{»heeeee*8eȿeeeve3h~eeevve&eeeeev ~eeeeevReeeeee ÿeeeet>heevee9~eeeet91¿heeeee9YeeetFeeeeeeTeeeeeZºÿeeeeeeeeeee"~eeeeee{eeeeetºeeeeee{eeeeee{eveee!{eeevee!¿eveee!{eeeev!~߀eeee+{ȼeeeeev{¿¼eveeee oeeeete{Ⱥeeeee RȼeeeetB¼eveee2eeeet2ceeeeee2;ȼeeeee9´eeeeeeȼeeeeee:eeveee5eeeeeeeeeeeU"eeeeeU~eeeeeU~´eeeeeUMeeeeetDeeeet ȼeeeeve¿eeeee~eeeeve8ߍ¼¹eeeeevߒ¼¼eeeevvߘ¸eeeeeeߑeeeeevߑ¿eeeee*ߒeeee3ߑºeeeee3ߑߓeeeee3߅eeeee=ߍeeeeeߘߘeeeee~ߋeeeee?????@??/?(@ ^rX7 *,Zd=EzTp^c{W^ NY^j^ (2U[tn<*2R_^$<Qm{q[SB1356899<X^ ex{:>;;9999999999998K]{&+125999999999999987751[s`L-/339999999999999923-,EihiLMDB977889999999999999431--^h/035599999999999998888;@@HAUm?HF@@?@:7788999998541.C[V}e.e__`ZZVTREG676/*<s{/d^^^^^^^^^^^Zby(,010/,w*h^^^^^^^^^^^]j "2Igpuz|}~}zwtlQCk^^^^^^^^^^^^QW9)Ut|~w0V. /i^^^^^^^^^^^^R B;du{ gLC >T[^^^^^^^^^^^Y\xzhn8MdYY^^^^^^^^^^^X!^|qboTaZ^^^^^^^^^^^Zf5k|`' ZU^^^^^^^^^^^^Y`:oeNU^^^^^^^^^^^^Y*o|egP^^^^^^^^^^^^[`vd&Y^^^^^^^^^^^cOrtY^^^^^^^^^^bB{tyiX^^^^^^^^^`Eoy1T^^^^^^^^^c#<uOS^^^^^^^^^Q ^}l iY^^^^^^^^^GtuK|\^^^^^^^]+9u\^^^^^^^\ `~j\^^^^^^^T i;[^^^^^^a)9pBZ^^^^^^a,7{A[^^^^^^a,7~n^^^^^^a,7l^^^^^^a,7l^^^^^^a,7^^^^^^b$=|^^^^^^cKz^^^^^^`?D]^^^^^^\~qG]^^^^^^]Qx*\^^^^^^^52~[^^^^^^^IziZ^^^^^^^S e'z[^^^^^^^"HunX^^^^^^_^xnpX^^^^^^^_NCV^^^^^^^^a'Cx R^^^^^^^^^ZcCR^^^^^^^^^_@$z\^^^^^^^^^r{sOZ^^^^^^^^X/?{X[^^^^^^^^|yoSX^^^^^^^yysNvW^^^^^}`y[ V^^^N^^~y7W|f^^,z~US~\+^~q3z\YYk{{[Qlj[^w???????(0`*./4:4?9DEQ@LRXYZ`QTaX[b_phij{k r 4%F<#:( ;) *9 PS=0CU( t1`?:M ]a.O[b+UV bjh i c lmqstuvwv mw}~zRRRRRRRRRRRRRiRRRRRRRRRRRRiRRi(RRRRRRRRRRRKRRRiR91RRRRRRRRRRRRRciii=<) RRRRRRRRRRRRRRRJE=RRRRRcRRRRRcRRRR*$RRRKKcRiOO3*<Rkl jmsbt s t m v}~|y ~zSSSSSeSSSSSeSSSeSSSSSeSSSeee`SCG-SSSeSSSSe`eeSH-"  SSSSSSSSS`SSeSC/+  SSKSSSSSS?:9+SS`SK:-+0A=SSSS=SS- 06=SSeeee^SSSSS:   *6CSSSSKS:XX//NK^SS?SKJ  9eSSS^^XWdsvvvvqh[NS^SJ   +eeSK3Ow~vcQKK   ?SKRs~vjSK  ?CZvxiKK9  6[~ieS*  Du~iKG  (fxSSH   EvSJ  #g~SSK5~qSK  T~SS?! gKK- #l¾SS6 #l»SSG lþvSeC )r¾SSJ #g¾eJ O»»SJ! 5SK+ )rxSS-  TSKA  #nS= ;SSC nS?~SeJ ~SJ+ ĉS_< ~S^NxA????( @*./4:4?9DE@LKRXYSZ`QaX[b_iypqhNjOJs >$nC ?%2.<) 28 "N]< 1\u2,V>Rkl+[l!5_q vw}~ ~zQQQ[QQQQQQ[[aQaI<QQQQ[QQaQQI81![KQQQQQQQ?7$ QQQI?89C!  &-97[B$ &+9W=YaaaQQaB $-8KKYUa9LL7QaQY\Q[aa[adJXjmoouj^6Ia ?aaObu}vePQa7aVhxo_a++Sm{`[+lQ; _oQQ 0oaQT~uKhvQQ$*oQ-(vjQQ4(tQ8 3tmQa8#jQ;fQ?M{QK!{QQ$:Qa1ga+vva6v{a8m{Q=}Qj????|(0,.24589:;FHMQSTUVXYZ\^_`defhmt^xz~J<&.:.;1>$D#F  0.bkjsz |RNNNNNRV NNNPVVVE6/" NNNPSTKB=7( NT5),?NNtcN0CtN4'qN9duDAxNV _sVlsNV#jxqT-ouyrm:?(,*,/12467:<>@AIJSUVWXZ[\]^_`abeYiQ sz8. KM<,/f?dNJMKKRLSf9\q y~<<<<<<:+<<<DD=DFQ]cddb\NA$3I[hpx|{yqgU?'B_kz|kX=- Zk~}jR<2@eyxa4Pli6TssS:WuuV?TvuV=%Mnr=()h~d=.OrtX7^uw]<8]omY9`fkke!H|<<<<<  ( :;?DJWY]^_ahi\lpvV wyG C( .D,J )O]ZLbfIw'x$z23444% /-,$1 )574(0:>BC@5bb-8DNSSLC6bb  4DRVZZWQ@bb#=KW\_^[UGbb*?PY^`a^ZO=b- >PX^`a]XM<b- 3GT[__\UFbbb>LUZZTJ;bb-bbbEIHD=bbbb$bbbbb:b9bbb  \( TZ^jVkXpr~Hw)0Jq>HNDY  GGGGGGGG  GG !GG(/2+'GG #4=?:3% .EFD8,G&9BE@6GG07;5-GG$G)G"G@@@@@(Rer`~.StT"**** **  ** **"#** '($*!()&**#%* ***1(8`!D@;jffffffffffffofffffffffffcfffffffffaoffff6ffffffffffafffffffffffffffff`fffffffffffffffffffffaffcffffffffffffffffffffaooffffffffffffffffffffffff330fffffffffffffffffffffff!"fffffffffffffffffffffffff`fffffffffffffffffffffffc6offffffffffffffffffffffff""fffffffffffffffffffffffafffffffffffffffffffffff66 fffcfffffffffffffffffff#2ffffffffffffffffffffffb"fffffffffffffffffff0f6ffffffffffffc36Q&ffffffffffffffc"!"&fffffffffffffffa33ffffffffffffffcffffffffcfffffffb316ffffffffffffff/fff`33fffffffffffffffffofffc&fffffffffffffffcfffffofffcfffffffffffffffffffffffffffc""6ffffffffffffffffffffffffffofffcC33fffffffffffffffffffffffffffff0fffcfffffffffffffffeGwwwwwww#fffffffoffcffffffffffffb5wwwwwwwwwwwwffffffffff!6ffffffffffcWwwxxxxxwwwUffffofff!6fffffffffeGwwxwtfffbffff!6ffffffff3wwxxwwr3fcfff!6fffffff2wwxxcffff!6ffffffdGwwwVofff!ffffffWxwwqfffafffffewfffafffffGwwfffaffffwfffafffbwwwfffafffwwOcffcffewxffffffxwfffffbwx˼fffffgxfffffwx˼˼˻ffffexx˼˻̼fff!w˼̼̼˻wfffaw̼fff`w̼̼fff1Gx̼̼˻fff`w˻fff1x̼˻fffcx̼̼fffb̼˻wfffcw̼˺fffbx̼˻fffcw̻fffb̻fffx̼˻ffffG˺fffG̼ffff!w̼̻6ffx˻ffffx̼fffax̼˻wfffa̻˻fffax˼̼fffax˼fffa̼fffax˻cfff˻ffff˻ffff˻˻ffff̻ffff˻ffff̻˻fff0̻fff1G̻fff0G˼˫fff1˻fff0˼˻fff2̻fffbx̼˺fffbx˻fffbH̼˺fffbG̼fffbW̼fffcx˻fff̻ffffx˻fffx̼ffffx˻fff˻fffax̼fffax̼fffaH̼fff`̼fffax̼˻fff`H̻˻ff6c̼ffffx̻̼fffcX̻ffff̼˺fffcx̼˻˻ffff̼̻ffff!̻̼̼fff!˼˻fff1˼̼̼˻fff!˻̼̼ffff!̻˻fff1fffaxfffafffbxfffafffafffaffffxffffffffffo??@P????????????????_?W(`8`@<Ppffoffffffffffffffffffffffffaffffffffffff1offffffffffffffffafffffffffffffffffff1ffffffffffffffffffffffffffffffffffffb"offffffffffffffffffccffffffffffffffffff`ffffffffffffffffff ffffffffffffffffc20fffffffffffffffffffffffff`f6ffffffffffb""ffffffffff30ffffffffffff6ffffffffffffoff136ffffffffffffffffa"&ffffffffffffffffffoffaffffffffffffffffffffff/ffafffffffffffbUUuuUU6fffffoffafffffffffdEwwwwwwwwt6fffoffaffffffff"wwxxxxwww6ffoffafffffffewwxwwfaffaffffff%wwxwCcffa&ffffewwwVfffffffwwwffffffgwxwwfffffdwxw_fffff'wwff!fawx_ff!fGw˼wfff!fw˻ff!gw˻ff!wx̻˻wff`w̼ffax˻ff`w˻˺ffaw̻ffcx̼ffcx˼˻ffcG˻wffcw̻fcw̻fffx˻ffx˫fffx̻ff˻ffaw˻ffaw˻ffaw̻ffaw˺ffbw˻ffax̪ffbw̻ffbw̻ffax̻fffx˻fffw̻fffx̺fffx̻ff1̻ff1̫ff1̻fff!˺ff1x̻ffax˻ff`G̼ffaG˺ffa̻fff˻fffw˻ffc˻fff̻fffH̻ff̼fffxʺffx˺fff1x˻ff`x̼̻ffa̼˼ffa˻˺ffa̼ffb˼ffbffbffbwffbxffffffffo?????@??/?(@7C`+=L?Z/X?22/TC2/C"!/30UT3UC3!#4D1!2UUX34"#4HX"""!3DU"""""""H#222O""""""X6tX""""""!vbX"""""""y_2""""""8YB""""""8˻˼2""""""8*̻˻""""""˻"!"""(:˺""""""$˺"""""")˺""""""j˺"""""!˺"""""&˺"""")̻U"""""˪2""""j̻"""""̻2""""˺2"""&˻2"""&̺B"""&̻R"""&̻R"""&̻B"""&̻"""&̻""")̻X"""!˯""""̺""""̻""""z˺""""̺""!"̻X1"""&B""""̻1""""̺B""""&̻"""""̻"""""˿"""""̻""""/̻"""̻"/˻""̻"/"?@????/(0`8`G-/Ia~wWwwww/wwwwwwww!wwuwuwwS!Auwwwwwww5"wwwwwwwu2wwwwwwww"uwws2%Ww3A3wwwww!'wwwwwwww!AUwwwwr""'www1wwwuHWwwQwwsiSwQwwiwQAwV˻wQu˻wqx˻qi˻wr̻r̻wI˯w̻w̻w̺w!˻w̻w!̻w1˯w1̻wp˯wq̻q̻wr˟rws˺uJ˹u˯uA˺wJ̼w̻w!̼w!˻wQ̻wQ˼wq@???((P=`N$ )Otww/wwww/wwwwwwudwwwwwd!0wwwwwwwT"0wwwwT"wwvB"Eww_t0"Ewwwwwv0EwwwDDFww_uGwwt9gw'wrOu'u)w!%S̻w!1$˻w!̻c ʯwaܻQ̺wq̺qܺwR9˯r19˿t9˯v19̪u ˿u9˿w8˯u#u!˯wAܺwA9̿SwQ̺qwR1˺r̺rvA????( @4`J ;*\:Ywwwwwwvwwwwwc1"!wwwwe1 wvU3!5_c !3Vgwwwa!"3gwwUUwwoq!wwcVqgssWu!S˻u!̻vܻw!̻wܻ*˯w2*˯1J̺wQJ˿bJ̺waJ˿q̿a˯c̻s!K̿s˿u̻u̻vv????|(06bQF%:hTA/eA?Bh"1EVfdFBQZ̻b˺˯:ܿ{̿{ݺ{̿!;̿ܿ!!̿B̼Ao?(,6^v$JG.TiUUU?UUU2UUU2AB3UUUUTfd5TAi̻!˹1ܺ1z˯QܯQܹQܹQܹR̿RjܯS˟UʟO|<<<<<  ( bA ,J]:"r{_UT2TC /S"#4_G̺Tܿlݺ l k 0 ݿ0˯@O_(^Q  `K{uu42!$3rGrgD˹s˯ܿ @ܯ0_  \( m \F9RGN e@D_Hqa!)ݺd;;9'ܿf/@@@@@(_r  /TtWTOR|T1mm3d-1.3.15/mm3dlogo.png000066400000000000000000000125001466047437300147040ustar00rootroot00000000000000PNG  IHDR=+= pHYs  d_IDATxO$IyƟȪ, 8'>XFy9 VlîWYq0KƂmY_X_^63;;S]CGѱoDV[mV[mV[fv۵feMjg¶W8yUM}S- oھv`!; ƍ{~<>;#HG38VhĶ g& )cB S[^^ BY 4ăq@h"PZR=}h;ӑ9b(ևЇp(525J5mdͦ@)S@@aϟ!r`>NWG^2J6P#J&~eY/2@5898g8'$`ox0.Qd0}(;zn+x=9A(̧'M/cPɺYP[6{py=p>〼(2"RđRj0A/59q(w= ̺ɨ"uA%G%C0cPJ:|Á$``m;b ̜*@RN  ![gc0ge0mNKa, ҦK6,~sq挠,K]˜rI^019s`Pv#^Mu#0g/xv}_YJ=Jjs@%ǯgv`w`]Oj?^ P."up[nMfc?yaVXxZCS/{(CP}c_s`5yʟt@#{N"'"䄙6S}+.-c%ޏ ٸ 5h-+nێKc\@2ʘ{d:v1hGXHV~ewu <(;0c]wmٍzC<|0J8tE(YQ<:2c  lO07W hI$5.HIu8Rzu?zK%OX^Jhp@*'YFc*WzDJ-s߫Id)\Jy z|Wd$a2;%AL)+W+]eU!8Ast=6!D-u6ҽҘIC}Cڶo컍ց ݨJy}ٶ "4JU4[gɰ5jɨ!<y: O]{Mh>Dl2>%$@b2I%̾TG 2ub"F%Y|*+o7uKSH7;++z@ dB9Z o TI~N-5I| x, KfGj>}=C /k>r YJx@"fx<@;vG#3%[*Kr|)g^%Y_L) M"˼&*}Qp9\G$7jaĀKZLsW'*0JjB-Z}(JG85mHV%% JSY5-QKCy 3GJ2lC$SIY)K[sJLX&Kf-ne%8&%30=?Z>~hPV%c0j L],h2j; \;dʶr4=ܞJĕҿD)h@qsJ:ԳL$TwN)_:,>hx ǒ ^ת'>7R̰Kw1W %]bζKKC *1`JQ˧#>i(O#OĮ@’PX9" +oQ)Oo=m͜Bl4QsP2REr&cukcK@'s/>Y(ǽN@ǶʺKjFM_4jBhNJ)o3 [u>5Ke᳀Yͼ9T*Z H|&b=_wpSg="tR 5 U&N-M{RNR)ؙ%KC,GbP!m8HNJ?NPxMfjڻ&)l Tc9R&Γ]TfrP[;}K0vP6meU\ Cĕ}$GF5' 8~s֭}]z BUYrkuj`$%(fn&մ;%dl?RȚl~౰Z"Ӿ6Ča(&'RI8&UkZ5-J_RM(@2!XJ6HP\xg%Sα=o >ecK=03d_61bR-"@;~bɾK i_uܕJ#Pz)ѱsTʔbhO{s攒롔2ws*u)ei\Yڏ5i#1Cnl24e-g]Ҏ 6/-K2[l}9dm KRˌHCLg^Mƭ cRJ bJ7T֞S<6B%Ɣ%j)}62t sɖSWQe FMf_J2'S²õc`JkR6~0꘻;ŖRIqdL)6dARL,ȨUĒlTK(iȭS@JcWn#7*jl vL]Ӓjč:!ۄRȾ T06aKEl%-XK w^ ڎA)Q8',2Sg+y)5}wO2@n#*9r+%U|?rVXz,F)%iq{ns-{pjJA,C+k!d쒜ɖ,|QʖLwbW%_c5k-}>mब{J FbP!M[ȠsP5sY6iV n6[(劵!\DV#-åB4윁Y2a\f+(?FIOma-\[ªS\Oʾo}i?b@,<%7 T*8Ijy#B8XzlBP\I1}TAmkA*lR-S_ 8}\]K6 I@-=rS&~UbP>7ŝ3)Ĕ"Y gc1v"?gZe*6%欔Zl('xZTAo?dAQ[[LO%3-t#|ln޷ܩ;UlYxZs.g>C4UiQ |B);u=nO6򚯒M>RKfHA\[d{fJnkD}oWF gPnH<$VW攕Qpndy5J>_^Y5BZ- ىIDDt1*6P_;,9lK\b[E5UL9}&u+np1F%SX̾d=TfNP3=wenmw6ȍbP˒/Z>͖x]nl2P|M17֬%;+pr&gS qQVIV};BX>TO|Z\) k0mJP> |;Bcv.N!bJ6 ,9 xr+k @V(-p]N#@. ;(&IEb}=kq@(oba5v Ԫ%U<څv dF8VE9i?rBTQKNƐl4}!>quj߉;)#0ߎG- ?X՘f붋 ܝ tyA*)hp9F-MFjŎ/0ޮP̝:|Qic*טec#7*~{ϪXԝ9< -~~#y6bu#B9P2ԵaUD5ddLz?~T('^t'wz Hdj1(jѩfV3oaP2j>A`\US~)Tџ剩@{_}4vB0nE^՘r *c'GWe=И [U]e߷g_ dߦ void segfault_handler( int sig ) { fprintf( stderr, "Segfault. Exiting...\n" ); exit( 0 ); } int free_memory() { return model_free_primitives(); } int main( int argc, char * argv[] ) { int rval = 0; #ifdef WIN32 // If started from command prompt, print messages there. // TODO: If AttachConsole() returns 0 and specified arguments // --debug, --warnings, or --errors run AllocConsole() //if ( AttachConsole( ATTACH_PARENT_PROCESS ) || ( want log output && AllocConsole() ) ) if ( AttachConsole( ATTACH_PARENT_PROCESS ) ) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); } #endif log_profile_init( "profile_data.txt" ); signal( SIGSEGV, segfault_handler ); init_sysconf(); transimp_install_translator(); init_prefs(); init_cmdline( argc, argv ); Q_INIT_RESOURCE( qtuiResources ); ui_prep( argc, argv ); { LOG_PROFILE(); { LOG_PROFILE_STR( "Initialize" ); // set up keyboard shortcuts std::string keycfgFile = getMm3dHomeDirectory(); keycfgFile += "/keycfg.in"; keycfg_load_file( keycfgFile.c_str() ); keycfg_set_defaults(); init_std_filters(); init_std_texture_filters(); init_std_tools(); init_std_cmds( CommandManager::getInstance() ); init_plugins(); model_status_register_function( StatusBar::getStatusBarFromModel ); } rval = ui_init( argc, argv ); { LOG_PROFILE_STR( "Uninitialize" ); std::string keycfgFile = getMm3dHomeDirectory(); keycfgFile += "/keycfg.out"; keycfg_save_file( keycfgFile.c_str() ); shutdown_cmdline(); model_show_alloc_stats(); DecalManager::release(); CommandManager::release(); FilterManager::release(); TextureManager::release(); PluginManager::release(); free_memory(); model_show_alloc_stats(); show_alloc_stats(); prefs_save(); } } log_profile_shutdown(); return rval; } mm3d-1.3.15/src/Makefile.am000066400000000000000000000021021466047437300152750ustar00rootroot00000000000000SUBDIRS = libmm3d mm3dcore depui qtui implui tools commands pixmap bin_PROGRAMS = mm3d EXTRA_DIST = maketags.sh win_resource.rc win_manifest.xml mm3d_HFILES = \ stdcmds.h \ stdfilters.h \ stdtexfilters.h \ stdtools.h mm3d_SOURCES = \ 3dm.cc \ stdcmds.cc \ stdfilters.cc \ stdtexfilters.cc \ stdtools.cc \ $(mm3d_HFILES) mm3d_LDADD = $(CORE_PROFILE) tools/libtools.a commands/libcommands.a implui/libimplui.a qtui/libqtui.a depui/libdepui.a mm3dcore/libmm3dcore.a libmm3d/libmm3d.a $(QGL_LIBS) $(QT_LIBS) $(LUALIB_LIBS) $(GL_LIBS) $(DLOPEN_LIBS) $(COVLFLAGS) mm3d_LDFLAGS = $(all_libraries) $(QT_CXXFLAGS) $(GL_CFLAGS) AM_CPPFLAGS = $(CORE_PROFILE) $(COVFLAGS) -Wall -I$(srcdir)/libmm3d -I$(srcdir)/mm3dcore -I$(srcdir)/depui -I$(builddir)/qtui -I$(srcdir)/implui -I$(srcdir)/tools -I$(srcdir)/commands -DMM3D_EDIT $(all_includes) $(QT_CXXFLAGS) $(LUALIB_CCFLAGS) $(GL_CFLAGS) %.moc.cc: %.h $(QT_MOC) -o $@ $< wc: wc `ls *.h *.cpp *.cc *.y *.l */*.h */*.cpp */*.cc */*.y */*.l 2> /dev/null | grep -v moc.cc | grep -v "\.base\." ` | sort -n CLEANFILES = *.gcno *.gcda mm3d-1.3.15/src/commands/000077500000000000000000000000001466047437300150475ustar00rootroot00000000000000mm3d-1.3.15/src/commands/Makefile.am000066400000000000000000000022301466047437300171000ustar00rootroot00000000000000noinst_LIBRARIES = libcommands.a libcommands_HFILES = \ aligncmd.h \ capcmd.h \ copycmd.h \ deletecmd.h \ dupcmd.h \ edgedivcmd.h \ edgeturncmd.h \ extrudecmd.h \ faceoutcmd.h \ flattencmd.h \ flipcmd.h \ hidecmd.h \ invertcmd.h \ invnormalcmd.h \ makefacecmd.h \ offsetcmd.h \ pastecmd.h \ rotatetexcmd.h \ selectfreecmd.h \ simplifycmd.h \ snapcmd.h \ spherifycmd.h \ subdividecmd.h \ unweldcmd.h \ weldcmd.h libcommands_a_SOURCES = \ aligncmd.cc \ capcmd.cc \ copycmd.cc \ deletecmd.cc \ dupcmd.cc \ edgedivcmd.cc \ edgeturncmd.cc \ extrudecmd.cc \ faceoutcmd.cc \ flattencmd.cc \ flipcmd.cc \ hidecmd.cc \ invertcmd.cc \ invnormalcmd.cc \ makefacecmd.cc \ offsetcmd.cc \ pastecmd.cc \ rotatetexcmd.cc \ selectfreecmd.cc \ simplifycmd.cc \ snapcmd.cc \ spherifycmd.cc \ subdividecmd.cc \ unweldcmd.cc \ weldcmd.cc \ $(libcommands_HFILES) AM_CPPFLAGS = $(CORE_PROFILE) $(COVFLAGS) -Wall -I$(srcdir)/../libmm3d -I$(srcdir)/../mm3dcore -I$(srcdir)/../depui -I$(builddir)/../qtui -I$(srcdir)/../implui -I$(srcdir)/../ -DMM3D_EDIT $(all_includes) $(QT_CXXFLAGS) $(LUALIB_CCFLAGS) $(GL_CFLAGS) CLEANFILES = *.gcno *.gcda mm3d-1.3.15/src/commands/aligncmd.cc000066400000000000000000000026101466047437300171330ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "aligncmd.h" #include "alignwin.h" #include "align.h" #include "log.h" #include #include AlignCommand::AlignCommand() { } AlignCommand::~AlignCommand() { } bool AlignCommand::activated( int arg, Model * model ) { AlignWin * win = new AlignWin( model ); win->show(); return true; } const char * AlignCommand::getPath() { return GEOM_MESHES_MENU; } const char * AlignCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Align Selected..." ); } mm3d-1.3.15/src/commands/aligncmd.h000066400000000000000000000025441466047437300170030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ALIGNCMD_H #define __ALIGNCMD_H #include "command.h" #include class AlignCommand : public Command { public: AlignCommand(); virtual ~AlignCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool isPrimitive() { return true; }; protected: }; #endif // __ALIGNCMD_H mm3d-1.3.15/src/commands/capcmd.cc000066400000000000000000000171321466047437300166110ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "capcmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include CapCommand::CapCommand() { } CapCommand::~CapCommand() { } bool CapCommand::activated( int arg, Model * model ) { int added = 0; // Algorithm: // // Selected edges // For each selected vertex, check every vertex connected to it // // For each connected edge that does not belong to two triangles, // add a triangle using a third vertex from another triangle // that uses this vertex and another selected vertex list vertList; list triList; // triangles using target vertex list conList; // vertices connected to target vertex model->getSelectedVertices( vertList ); std::list::iterator it; for ( it = vertList.begin(); it != vertList.end(); it++ ) { getConnected( model, *it, conList, triList ); unsigned int vcount = conList.size(); unsigned int tcount = triList.size(); if ( vcount > 2 && vcount != tcount ) { // If I'm connected to more than two vertices, the // triangle and vertex count should match, otherwise // there is a gap/hole. int newTri = 0; while ( tcount != vcount && newTri >= 0 ) { newTri = createMissingTriangle( model, *it, conList, triList ); if ( newTri >= 0 ) { added++; tcount++; triList.push_back( newTri ); } } } } if ( added != 0 ) { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Cap Holes complete").toUtf8().data() ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "Could not find gap in selected region" ).toUtf8().data() ); } return false; } void CapCommand::getConnected( Model * model, int v, std::list & conList, std::list & triList ) { conList.clear(); triList.clear(); unsigned int tcount = model->getTriangleCount(); for ( unsigned int tri = 0; tri < tcount; tri++ ) { unsigned int verts[3]; model->getTriangleVertices( tri, verts[0], verts[1], verts[2] ); for ( int i = 0; i < 3; i++ ) { if ( (int) verts[i] == v ) { triList.push_back( tri ); addToList( conList, v, verts[0] ); addToList( conList, v, verts[1] ); addToList( conList, v, verts[2] ); break; } } } } void CapCommand::addToList( std::list & l, int ignore, int val ) { if ( ignore == val ) { return; } std::list::iterator it; for ( it = l.begin(); it != l.end(); it++ ) { if ( *it == val ) { return; } } l.push_back( val ); } int CapCommand::createMissingTriangle( Model * model, unsigned int v, std::list & conList, std::list & triList ) { std::list::iterator cit1; std::list::iterator cit2; int tri = 0; for ( cit1 = conList.begin(); cit1 != conList.end(); cit1++ ) { if ( model->isVertexSelected( *cit1 ) ) { if ( triangleCount( model, v, *cit1, triList, tri ) == 1 ) { cit2 = cit1; cit2++; // Find third triangle vertex for normal test below int tri1Vert = 0; unsigned int verts[3]; model->getTriangleVertices( tri, verts[0], verts[1], verts[2] ); for ( int i = 0; i < 3; i++ ) { if ( verts[i] != v && verts[i] != (unsigned int) *cit1 ) { tri1Vert = verts[i]; } } for ( ; cit2 != conList.end(); cit2++ ) { if ( model->isVertexSelected( *cit2 ) ) { if ( triangleCount( model, v, *cit2, triList, tri ) == 1 ) { int newTri = model->addTriangle( v, *cit1, *cit2 ); float norm1[3]; float norm2[3]; double coord[3]; model->getFlatNormal( tri, norm1 ); model->getFlatNormal( newTri, norm2 ); model->getVertexCoords( *cit1, coord ); // this is why we needed the third vertex above, // so we can check to see if we need to invert // the new triangle (are the triangles behind // each other or in front of each other?) float f = model->cosToPoint( tri, coord ); if ( fabs( f ) < 0.0001f ) { // Points are nearly co-planar, // normals should face the same way if ( dot3( norm1, norm2 ) < 0.0f ) { model->invertNormals( newTri ); } } else { if ( f < 0.0f ) { model->getVertexCoords( tri1Vert, coord ); if ( model->cosToPoint( newTri, coord ) > 0.0f ) { model->invertNormals( newTri ); } } else { model->getVertexCoords( tri1Vert, coord ); if ( model->cosToPoint( newTri, coord ) < 0.0f ) { model->invertNormals( newTri ); } } } return newTri; } } } } } } return -1; } int CapCommand::triangleCount( Model * model, unsigned int v1, unsigned int v2, std::list & triList, int & tri ) { std::list::iterator it; unsigned int verts[3]; int triCount = 0; for ( it = triList.begin(); it != triList.end(); it++ ) { model->getTriangleVertices( *it, verts[0], verts[1], verts[2] ); bool have1 = false; bool have2 = false; for ( int i = 0; i < 3; i++ ) { if ( verts[i] == v1 ) have1 = true; else if ( verts[i] == v2 ) have2 = true; } if ( have1 && have2 ) { triCount++; tri = *it; } } return triCount; } const char * CapCommand::getPath() { return GEOM_MESHES_MENU; } const char * CapCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Cap Holes" ); } mm3d-1.3.15/src/commands/capcmd.h000066400000000000000000000033331466047437300164510ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CAPCMD_H #define __CAPCMD_H #include "command.h" #include class CapCommand : public Command { public: CapCommand(); virtual ~CapCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: void getConnected( Model * model, int vert, std::list & conList, std::list & triList); void addToList( std::list & l, int ignore, int val ); int createMissingTriangle( Model * model, unsigned int v, std::list & conList, std::list & triList ); int triangleCount( Model * model, unsigned int v1, unsigned int v2, std::list & triList, int & tri ); }; #endif // __CAPCMD_H mm3d-1.3.15/src/commands/copycmd.cc000066400000000000000000000044151466047437300170200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "copycmd.h" #include "model.h" #include "filtermgr.h" #include "texmgr.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include "sysconf.h" #include "misc.h" #include #include #include #include using std::list; using std::map; CopyCommand::CopyCommand() { } CopyCommand::~CopyCommand() { } bool CopyCommand::activated( int arg, Model * model ) { if ( !model ) return false; if ( model->getSelectedTriangleCount() == 0 && model->getSelectedPointCount() == 0 && model->getSelectedProjectionCount() == 0 ) { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have at least 1 face, joint, or point selected to Copy" ).toUtf8().data() ); return false; } Model * m = model->copySelected(); if ( !m ) return false; model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Selected primitives copied" ).toUtf8().data() ); std::string clipfile = getMm3dHomeDirectory(); clipfile += "/clipboard"; mkpath( clipfile.c_str(), 0755 ); clipfile += "/clipboard.mm3d"; FilterManager::getInstance()->writeFile( m, clipfile.c_str(), false, FilterManager::WO_ModelNoPrompt ); delete m; return true; } const char * CopyCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Copy Selected to Clipboard" ); } mm3d-1.3.15/src/commands/copycmd.h000066400000000000000000000025401466047437300166570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __COPYCMD_H #define __COPYCMD_H #include "command.h" #include class CopyCommand : public Command { public: CopyCommand(); virtual ~CopyCommand(); int getCommandCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::CTRL+Qt::Key_C; return true; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __COPYCMD_H mm3d-1.3.15/src/commands/deletecmd.cc000066400000000000000000000047051466047437300173120ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include #include #include #include "deletecmd.h" #include "model.h" #include "msg.h" #include "modelstatus.h" DeleteCommand::DeleteCommand() { } DeleteCommand::~DeleteCommand() { } const char * DeleteCommand::getName( int arg ) { if ( arg == 0 ) { return QT_TRANSLATE_NOOP( "Command", "Delete" ); } else { return "[Out of range]"; } } bool DeleteCommand::getKeyBinding( int arg, int & keyBinding ) { if ( arg == 0 ) { keyBinding = Qt::Key_Delete; return true; } else { return false; } } bool DeleteCommand::activated( int arg, Model * model ) { if ( arg == 0 && model ) { static bool warnedAlready = false; std::list joints; model->getSelectedBoneJoints( joints ); bool doDelete = true; if ( model->getAnimationMode() == Model::ANIMMODE_NONE && joints.size() > 0 && model->getAnimCount( Model::ANIMMODE_SKELETAL ) > 0 && !warnedAlready ) { QString s = qApp->translate( "Command", "Deleting joints may destroy skeletal animations\nDo you wish to continue?" ); if ( msg_warning_prompt( (const char *) s.toUtf8(), "yN" ) == 'Y' ) { warnedAlready = true; } else { doDelete = false; } } if ( doDelete ) { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Primitives deleted" ).toUtf8().data() ); model->deleteSelected(); } return doDelete; } else { return false; } } mm3d-1.3.15/src/commands/deletecmd.h000066400000000000000000000024131466047437300171460ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __DELETECMD_H #define __DELETECMD_H #include "command.h" class DeleteCommand : public Command { public: DeleteCommand(); virtual ~DeleteCommand(); int getCommandCount() { return 1; }; bool getKeyBinding( int arg, int & keyBinding ); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; }; #endif // __DELETECMD_H mm3d-1.3.15/src/commands/dupcmd.cc000066400000000000000000000173511466047437300166410ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "dupcmd.h" #include "model.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include #include #include #include using std::list; using std::map; DuplicateCommand::DuplicateCommand() { } DuplicateCommand::~DuplicateCommand() { } bool DuplicateCommand::activated( int arg, Model * model ) { if ( model ) { list tri; model->getSelectedTriangles( tri ); list joints; model->getSelectedBoneJoints( joints ); list points; model->getSelectedPoints( points ); list vert; map vertMap; map triMap; map jointMap; map pointMap; list::iterator lit; if ( !joints.empty() ) { // Duplicated joints log_debug( "Duplicating %" PORTuSIZE " joints\n", joints.size() ); for ( lit = joints.begin(); lit != joints.end(); lit++ ) { int parent = model->getBoneJointParent( *lit ); // TODO this will not work if parent joint comes after child // joint. That shouldn't happen... but... if ( model->isBoneJointSelected( parent ) ) { parent = jointMap[ parent ]; } // If joint is root joint, assign duplicated joint to be child // of original if ( parent == -1 ) { parent = 0; } double coord[3]; double rot[3] = { 0, 0, 0 }; model->getBoneJointCoords( *lit, coord ); int nj = model->addBoneJoint( model->getBoneJointName( *lit ), coord[0], coord[1], coord[2], rot[0], rot[1], rot[2], parent ); jointMap[ *lit ] = nj; } } if ( !tri.empty() ) { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Selected primitives duplicated" ).toUtf8().data() ); model->getSelectedVertices( vert ); // Duplicated vertices log_debug( "Duplicating %" PORTuSIZE " vertices\n", vert.size() ); for ( lit = vert.begin(); lit != vert.end(); lit++ ) { double coords[3]; model->getVertexCoords( *lit, coords ); int nv = model->addVertex( coords[0], coords[1], coords[2] ); if ( model->isVertexFree( *lit ) ) { model->setVertexFree( nv, true ); } Model::InfluenceList il; Model::InfluenceList::iterator it; model->getVertexInfluences( *lit, il ); for ( it = il.begin(); it != il.end(); it++ ) { int joint = it->m_boneId; if ( model->isBoneJointSelected( joint ) ) { joint = jointMap[ joint ]; } model->addVertexInfluence( nv, joint, it->m_type, it->m_weight ); } vertMap[ *lit ] = nv; } // Duplicate faces log_debug( "Duplicating %" PORTuSIZE " faces\n", tri.size() ); for ( lit = tri.begin(); lit != tri.end(); lit++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = model->getTriangleVertex( *lit, t ); } int nt = model->addTriangle( vertMap[v[0]] , vertMap[v[1]], vertMap[v[2]] ); triMap[ *lit ] = nt; } // Duplicate texture coords log_debug( "Duplicating %" PORTuSIZE " face texture coordinates\n", tri.size() ); for ( lit = tri.begin(); lit != tri.end(); lit++ ) { float s; float t; for ( unsigned i = 0; i < 3; i++ ) { model->getTextureCoords( (unsigned) *lit, i, s, t ); model->setTextureCoords( (unsigned) triMap[ *lit ], i, s, t ); } } if ( model->getGroupCount() ) { // Set groups log_debug( "Setting %" PORTuSIZE " triangle groups\n", tri.size() ); for ( lit = tri.begin(); lit != tri.end(); lit++ ) { // This works, even if triangle group == -1 int gid = model->getTriangleGroup(*lit); if ( gid >= 0 ) { model->addTriangleToGroup( gid, triMap[*lit] ); } } } } if ( !points.empty() ) { // Duplicated points log_debug( "Duplicating %" PORTuSIZE " points\n", points.size() ); for ( lit = points.begin(); lit != points.end(); lit++ ) { double coord[3]; double rot[3] = { 0, 0, 0 }; model->getPointCoords( *lit, coord ); model->getPointRotation( *lit, rot ); int np = model->addPoint( model->getPointName( *lit ), coord[0], coord[1], coord[2], rot[0], rot[1], rot[2], -1 ); Model::InfluenceList il; Model::InfluenceList::iterator it; model->getPointInfluences( *lit, il ); for ( it = il.begin(); it != il.end(); it++ ) { int joint = it->m_boneId; if ( model->isBoneJointSelected( joint ) ) { joint = jointMap[ joint ]; } model->addPointInfluence( np, joint, it->m_type, it->m_weight ); } pointMap[ *lit ] = np; } } model->unselectAll(); // Select vertices log_debug( "reselecting vertices\n" ); for ( lit = vert.begin(); lit != vert.end(); lit++ ) { model->selectVertex( vertMap[*lit] ); } // Select faces log_debug( "reselecting faces\n" ); for ( lit = tri.begin(); lit != tri.end(); lit++ ) { model->selectTriangle( triMap[*lit] ); } // Select bone joints log_debug( "reselecting bone joints\n" ); for ( lit = joints.begin(); lit != joints.end(); lit++ ) { model->selectBoneJoint( jointMap[*lit] ); } // Select points log_debug( "reselecting points\n" ); for ( lit = points.begin(); lit != points.end(); lit++ ) { model->selectPoint( pointMap[*lit] ); } model->invalidateNormals(); if ( joints.empty() && tri.empty() && points.empty() ) { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have at least 1 face, joint, or point selected to Duplicate" ).toUtf8().data() ); return false; } else { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Duplicate complete" ).toUtf8().data() ); } return true; } else { return false; } } const char * DuplicateCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Duplicate" ); } mm3d-1.3.15/src/commands/dupcmd.h000066400000000000000000000025541466047437300165020ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __DUPCMD_H #define __DUPCMD_H #include "command.h" #include class DuplicateCommand : public Command { public: DuplicateCommand(); virtual ~DuplicateCommand(); int getCommandCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::CTRL+Qt::Key_D; return true; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __DUPCMD_H mm3d-1.3.15/src/commands/edgedivcmd.cc000066400000000000000000000115571466047437300174620ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "edgedivcmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include EdgeDivideCommand::EdgeDivideCommand() { } EdgeDivideCommand::~EdgeDivideCommand() { } bool EdgeDivideCommand::activated( int arg, Model * model ) { int split = 0; unsigned int newVertex = ~0U; list vertList; model->getSelectedVertices( vertList ); if ( vertList.size() == 2 ) { // Only divides one edge, deal with it unsigned int v1 = vertList.front(); vertList.pop_front(); unsigned int v2 = vertList.front(); // The triangle count will grow while we're iterating, but that's okay // because we know that the new triangles don't need to be split unsigned int tcount = model->getTriangleCount(); for ( unsigned int tri = 0; tri < tcount; tri++ ) { const int INVALID = ~0; int a = INVALID; int b = INVALID; int c = INVALID; unsigned int tv[3] = {0,0,0}; model->getTriangleVertices( tri, tv[0], tv[1], tv[2] ); for ( int i = 0; i < 3; i++ ) { if ( tv[i] == v1 ) a = i; else if ( tv[i] == v2 ) b = i; else c = i; } // don't assume 'c' is set, triangle may have one vertex // assigned to two corners if ( a != INVALID && b != INVALID && c != INVALID ) { if ( split == 0 ) { double coord1[3]; double coord2[3]; model->getVertexCoords( v1, coord1 ); model->getVertexCoords( v2, coord2 ); for ( int i = 0; i < 3; i++ ) { coord1[i] = (coord1[i] + coord2[i]) / 2.0; } newVertex = model->addVertex( coord1[0], coord1[1], coord1[2] ); } float oldST[3][2]; for ( int i = 0; i < 3; i++ ) { model->getTextureCoords( tri, i, oldST[i][0], oldST[i][1] ); } int g = model->getTriangleGroup( tri ); int vert[3]; vert[ a ] = newVertex; vert[ b ] = tv[b]; vert[ c ] = tv[c]; int newTri = model->addTriangle( vert[0], vert[1], vert[2] ); if ( g >= 0 ) { model->addTriangleToGroup( g, newTri ); } vert[ a ] = tv[a]; vert[ b ] = newVertex; vert[ c ] = tv[c]; model->setTriangleVertices( tri, vert[0], vert[1], vert[2] ); float st[3][2]; for ( int i = 0; i < 2; i++ ) { st[a][i] = ( oldST[a][i] + oldST[b][i] ) / 2.0f; st[b][i] = oldST[b][i]; st[c][i] = oldST[c][i]; } for ( int i = 0; i < 3; i++ ) { model->setTextureCoords( newTri, i, st[i][0], st[i][1] ); } for ( int i = 0; i < 2; i++ ) { st[a][i] = oldST[a][i]; st[b][i] = ( oldST[a][i] + oldST[b][i] ) / 2.0f; st[c][i] = oldST[c][i]; } for ( int i = 0; i < 3; i++ ) { model->setTextureCoords( tri, i, st[i][0], st[i][1] ); } split++; } } } if ( split > 0 ) { model->unselectAllVertices(); model->selectVertex( newVertex ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Edge Divide complete" ).toUtf8().data() ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have 2 adjacent vertices selected to Edge Divide" ).toUtf8().data() ); } return false; } const char * EdgeDivideCommand::getPath() { return GEOM_FACES_MENU; } const char * EdgeDivideCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Edge Divide" ); } mm3d-1.3.15/src/commands/edgedivcmd.h000066400000000000000000000024201466047437300173110ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __EDGEDIVCMD_H #define __EDGEDIVCMD_H #include "command.h" class EdgeDivideCommand : public Command { public: EdgeDivideCommand(); virtual ~EdgeDivideCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __EDGEDIVCMD_H mm3d-1.3.15/src/commands/edgeturncmd.cc000066400000000000000000000137631466047437300176710ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "edgeturncmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include EdgeTurnCommand::EdgeTurnCommand() { } EdgeTurnCommand::~EdgeTurnCommand() { } bool EdgeTurnCommand::activated( int arg, Model * model ) { list triList; model->getSelectedTriangles( triList ); if ( triList.size() >= 2 ) { // Only turns one edge, deal with it unsigned int edge_v1; unsigned int edge_v2; unsigned int tri1_v; unsigned int tri2_v; list::iterator it1 = triList.begin(); list::iterator it2 = it1; for ( it1 = triList.begin(); it1 != triList.end(); it1++ ) { it2 = it1; it2++; for ( ; it2 != triList.end(); it2++ ) { if ( canTurnEdge( model, *it1, *it2, edge_v1, edge_v2, tri1_v, tri2_v ) ) { log_debug( "turning edge on triangles %d and %d\n", *it1, *it2 ); log_debug( "vertices %d,%d,%d %d,%d,%d\n", edge_v1, edge_v2, tri1_v, edge_v1, edge_v2, tri2_v ); int t1a = 0; int t1b = 0; int t1c = 0; int t2a = 0; int t2b = 0; int t2c = 0; getTriangleVertices( model, *it1, edge_v1, edge_v2, tri1_v, t1a, t1b, t1c ); getTriangleVertices( model, *it2, edge_v1, edge_v2, tri2_v, t2a, t2b, t2c ); log_debug( "indices %d,%d,%d %d,%d,%d\n", t1a, t1b, t1c, t2a, t2b, t2c ); // For triangle 1: unsigned int verts1[ 3 ]; unsigned int verts2[3]; verts1[ t1a ] = tri1_v; verts1[ t1b ] = tri2_v; verts1[ t1c ] = edge_v2; verts2[ t2a ] = tri1_v; verts2[ t2b ] = tri2_v; verts2[ t2c ] = edge_v1; model->setTriangleVertices( *it1, verts1[0], verts1[1], verts1[2] ); model->setTriangleVertices( *it2, verts2[0], verts2[1], verts2[2] ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Edge Turn complete" ).toUtf8().data() ); return true; } } } } model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have at least 2 adjacent faces to Edge Turn" ).toUtf8().data() ); return false; } // edge_v1 and edge_v2 are the model vertices of the common edge // tri1_v and tri2_v are the opposite vertices of the respective triangles bool EdgeTurnCommand::canTurnEdge( Model * model, int tri1, int tri2, unsigned int & edge_v1, unsigned int & edge_v2, unsigned int & tri1_v, unsigned int & tri2_v ) { unsigned int verts1[3]; unsigned int verts2[3]; model->getTriangleVertices( tri1, verts1[0], verts1[1], verts1[2] ); model->getTriangleVertices( tri2, verts2[0], verts2[1], verts2[2] ); const unsigned int invalid = (unsigned) ~0; edge_v1 = invalid; edge_v2 = invalid; tri1_v = invalid; tri2_v = invalid; for ( int i1 = 0; i1 < 3; i1++ ) { unsigned int v1 = verts1[i1]; if ( v1 != edge_v1 && v1 != edge_v2 ) { for ( int i2 = 0; i2 < 3; i2++ ) { unsigned int v2 = verts2[i2]; if ( v2 != edge_v1 && v2 != edge_v2 ) { if ( v1 == v2 ) { if ( edge_v1 == invalid ) edge_v1 = v1; else if ( edge_v2 == invalid ) edge_v2 = v1; } } } } } if ( edge_v1 != invalid && edge_v2 != invalid ) { int i; for ( i = 0; i < 3; i++ ) { if ( verts1[i] != edge_v1 && verts1[i] != edge_v2 ) { tri1_v = verts1[i]; break; } } for ( i = 0; i < 3; i++ ) { if ( verts2[i] != edge_v1 && verts2[i] != edge_v2 ) { tri2_v = verts2[i]; break; } } return true; } else { return false; } } // Triangle index a is edge_v1 // Triangle index b is edge_v2 // Triangle index c is tri_v, the third triangle vertex // // Note that a, b, and c are the indices into the list of three triangle vertices, // not the index of the model vertices void EdgeTurnCommand::getTriangleVertices( Model * model, int tri, unsigned int edge_v1, unsigned int edge_v2, unsigned int tri_v, int & a, int & b, int & c ) { unsigned int v[3] = {0,0,0}; model->getTriangleVertices( tri, v[0], v[1], v[2] ); for ( int i = 0; i < 3; i++ ) { if ( v[i] == edge_v1 ) a = i; else if ( v[i] == edge_v2 ) b = i; else if ( v[i] == tri_v ) c = i; } } const char * EdgeTurnCommand::getPath() { return GEOM_FACES_MENU; } const char * EdgeTurnCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Edge Turn" ); } mm3d-1.3.15/src/commands/edgeturncmd.h000066400000000000000000000031411466047437300175200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __EDGETURNCMD_H #define __EDGETURNCMD_H #include "command.h" class EdgeTurnCommand : public Command { public: EdgeTurnCommand(); virtual ~EdgeTurnCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: bool canTurnEdge( Model * model, int tri1, int tri2, unsigned int & edge_v1, unsigned int & edge_v2, unsigned int & tri1_v, unsigned int & tri2_v ); void getTriangleVertices( Model * model, int tri, unsigned int edge_v1, unsigned int edge_v2, unsigned int tri_v, int & a, int & b, int & c ); }; #endif // __EDGETURNCMD_H mm3d-1.3.15/src/commands/extrudecmd.cc000066400000000000000000000024641466047437300175300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "extrudecmd.h" #include "extrudewin.h" #include "log.h" #include #include ExtrudeCommand::ExtrudeCommand() { } ExtrudeCommand::~ExtrudeCommand() { } bool ExtrudeCommand::activated( int arg, Model * model ) { ExtrudeWin * win = new ExtrudeWin( model ); win->exec(); delete win; return true; } const char * ExtrudeCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Extrude..." ); } mm3d-1.3.15/src/commands/extrudecmd.h000066400000000000000000000025561466047437300173740ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __EXTRUDECMD_H #define __EXTRUDECMD_H #include "command.h" #include class ExtrudeCommand : public Command { public: ExtrudeCommand(); virtual ~ExtrudeCommand(); int getCommandCount() { return 1; }; const char * getName( int arg ); bool activated( int arg, Model * model ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_Insert; return true; }; bool isPrimitive() { return true; }; protected: }; #endif // __EXTRUDECMD_H mm3d-1.3.15/src/commands/faceoutcmd.cc000066400000000000000000000032771466047437300175010ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "faceoutcmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include #include using std::list; FaceOutCommand::FaceOutCommand() { } FaceOutCommand::~FaceOutCommand() { } bool FaceOutCommand::activated( int arg, Model * model ) { // TODO this is slow, see if we can speed it up list faces; model->getSelectedTriangles( faces ); list::iterator it; for ( it = faces.begin(); it != faces.end(); it++ ) { if ( model->triangleFacesIn( *it ) ) { model->invertNormals( *it ); } } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Normals Face Out" ).toUtf8().data() ); return true; } const char * FaceOutCommand::getPath() { return GEOM_NORMALS_MENU; } mm3d-1.3.15/src/commands/faceoutcmd.h000066400000000000000000000025611466047437300173360ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __FACEOUTCMD_H #define __FACEOUTCMD_H #include "command.h" class FaceOutCommand : public Command { public: FaceOutCommand(); virtual ~FaceOutCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ) { return "Normals Face Out"; }; bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __FACEOUTCMD_H mm3d-1.3.15/src/commands/flattencmd.cc000066400000000000000000000055151466047437300175050ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "flattencmd.h" #include "model.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include #include #include using std::list; FlattenCommand::FlattenCommand() { } FlattenCommand::~FlattenCommand() { } const char * FlattenCommand::getName( int arg ) { switch ( arg ) { case 0: return QT_TRANSLATE_NOOP( "Command", "Flatten" ); break; case 1: return QT_TRANSLATE_NOOP( "Command", "Flatten X" ); break; case 2: return QT_TRANSLATE_NOOP( "Command", "Flatten Y" ); break; case 3: return QT_TRANSLATE_NOOP( "Command", "Flatten Z" ); break; default: break; } return "[Out of range]"; } bool FlattenCommand::activated( int arg, Model * model ) { int index; index = arg - 1; // Check for index out of range if ( index < 0 || index > 2 ) { log_error( "flatten on index %d out of range", index ); return false; } list posList; model->getSelectedPositions( posList ); if ( posList.empty() ) { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "Need at least 1 vertex, joint, point, or face selected" ).toUtf8().data() ); return false; } float newVal = 0.0f; int countVal = 0; double coords[3]; list::iterator it; for ( it = posList.begin(); it != posList.end(); it++ ) { model->getPositionCoords( *it, coords ); newVal += coords[index]; countVal++; } newVal = newVal / (float) countVal; for ( it = posList.begin(); it != posList.end(); it++ ) { model->getPositionCoords( *it, coords ); coords[index] = newVal; model->movePosition( *it, coords[0], coords[1], coords[2] ); } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Selected primitives flattened" ).toUtf8().data() ); return true; } mm3d-1.3.15/src/commands/flattencmd.h000066400000000000000000000024631466047437300173460ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __FLATTENCMD_H #define __FLATTENCMD_H #include "command.h" class FlattenCommand : public Command { public: FlattenCommand(); virtual ~FlattenCommand(); int getCommandCount() { return 4; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __FLATTENCMD_H mm3d-1.3.15/src/commands/flipcmd.cc000066400000000000000000000055011466047437300167750ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "flipcmd.h" #include "model.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include #include #include using std::list; FlipCommand::FlipCommand() { } FlipCommand::~FlipCommand() { } const char * FlipCommand::getName( int arg ) { switch ( arg ) { case 0: return QT_TRANSLATE_NOOP( "Command", "Flip" ); break; case 1: return QT_TRANSLATE_NOOP( "Command", "Flip X" ); break; case 2: return QT_TRANSLATE_NOOP( "Command", "Flip Y" ); break; case 3: return QT_TRANSLATE_NOOP( "Command", "Flip Z" ); break; default: break; } return "[Out of range]"; } bool FlipCommand::activated( int arg, Model * model ) { int index; index = arg - 1; // Check for index out of range if ( index < 0 || index > 2 ) { log_error( "flip on index %d out of range", index ); return false; } list posList; model->getSelectedPositions( posList ); if ( posList.empty() ) { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "Need at least 1 vertex, joint, point, or face selected" ).toUtf8().data() ); return false; } { list::iterator it; for ( it = posList.begin(); it != posList.end(); it++ ) { double coords[3]; model->getPositionCoords( *it, coords ); coords[index] = -coords[index]; model->movePosition( *it, coords[0], coords[1], coords[2] ); } } { list face; list::iterator it; model->getSelectedTriangles( face ); for ( it = face.begin(); it != face.end(); it++ ) { model->invertNormals( *it ); } } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Selected primitives flipped" ).toUtf8().data() ); return true; } mm3d-1.3.15/src/commands/flipcmd.h000066400000000000000000000024411466047437300166370ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __FLIPCMD_H #define __FLIPCMD_H #include "command.h" class FlipCommand : public Command { public: FlipCommand(); virtual ~FlipCommand(); int getCommandCount() { return 4; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __FLIPCMD_H mm3d-1.3.15/src/commands/hidecmd.cc000066400000000000000000000052251466047437300167570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "hidecmd.h" #include "model.h" #include "modelstatus.h" #include #include #include HideCommand::HideCommand() { } HideCommand::~HideCommand() { } const char * HideCommand::getName( int arg ) { switch ( arg ) { case 0: return QT_TRANSLATE_NOOP( "Command", "Hide" ); case 1: return QT_TRANSLATE_NOOP( "Command", "Hide Unselected" ); case 2: return QT_TRANSLATE_NOOP( "Command", "Hide Selected" ); case 3: return QT_TRANSLATE_NOOP( "Command", "Unhide All" ); default: break; } return "[Out of range]"; } bool HideCommand::getKeyBinding( int arg, int & keyBinding ) { switch ( arg ) { case 0: break; case 1: keyBinding = Qt::Key_H; return true; case 2: keyBinding = Qt::Key_H + Qt::SHIFT; return true; case 3: keyBinding = Qt::Key_U; return true; default: break; } return false; } bool HideCommand::activated( int arg, Model * model ) { switch ( arg ) { case 2: model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Selected primitives hidden" ).toUtf8().data() ); model->hideSelected(); return true; break; case 3: model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Primitives unhidden" ).toUtf8().data() ); model->unhideAll(); return true; break; default: model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Unselected primitives hidden" ).toUtf8().data() ); model->hideUnselected(); return true; break; } return false; } mm3d-1.3.15/src/commands/hidecmd.h000066400000000000000000000024161466047437300166200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __HIDECMD_H #define __HIDECMD_H #include "command.h" class HideCommand : public Command { public: HideCommand(); virtual ~HideCommand(); int getCommandCount() { return 4; }; bool getKeyBinding( int arg, int & keyBinding ); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __HIDECMD_H mm3d-1.3.15/src/commands/invertcmd.cc000066400000000000000000000030501466047437300173470ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "invertcmd.h" #include "model.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include #include InvertSelectionCommand::InvertSelectionCommand() { } InvertSelectionCommand::~InvertSelectionCommand() { } bool InvertSelectionCommand::activated( int arg, Model * model ) { if ( model ) { model->invertSelection(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Selection inverted" ).toUtf8().data() ); return true; } else { return false; } } const char * InvertSelectionCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Invert Selection" ); } mm3d-1.3.15/src/commands/invertcmd.h000066400000000000000000000024671466047437300172240ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __INVERTCMD_H #define __INVERTCMD_H #include "command.h" class InvertSelectionCommand : public Command { public: InvertSelectionCommand(); virtual ~InvertSelectionCommand(); int getCommandCount() { return 1; }; const char * getName(int arg); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; }; #endif // __INVERTCMD_H mm3d-1.3.15/src/commands/invnormalcmd.cc000066400000000000000000000033441466047437300200530ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "invnormalcmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include #include using std::list; InvertNormalCommand::InvertNormalCommand() { } InvertNormalCommand::~InvertNormalCommand() { } bool InvertNormalCommand::activated( int arg, Model * model ) { list faces; model->getSelectedTriangles( faces ); list::iterator it; for ( it = faces.begin(); it != faces.end(); it++ ) { model->invertNormals( *it ); } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Normals inverted" ).toUtf8().data() ); return true; } const char * InvertNormalCommand::getPath() { return GEOM_NORMALS_MENU; } const char * InvertNormalCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Invert Normals" ); } mm3d-1.3.15/src/commands/invnormalcmd.h000066400000000000000000000025471466047437300177210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __INVNORMALCMD_H #define __INVNORMALCMD_H #include "command.h" class InvertNormalCommand : public Command { public: InvertNormalCommand(); virtual ~InvertNormalCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __INVNORMALCMD_H mm3d-1.3.15/src/commands/makefacecmd.cc000066400000000000000000000046601466047437300176040ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "makefacecmd.h" #include "model.h" #include "msg.h" #include "modelstatus.h" #include "cmdmgr.h" #include "log.h" #include #include MakeFaceCommand::MakeFaceCommand() { } MakeFaceCommand::~MakeFaceCommand() { } const char * MakeFaceCommand::getPath() { return GEOM_VERTICES_MENU; } const char * MakeFaceCommand::getName( int arg ) { if ( arg == 0 ) { return QT_TRANSLATE_NOOP( "Command", "Make Face From Vertices" ); } else { return "[Out of range]"; } } bool MakeFaceCommand::getKeyBinding( int arg, int & keyBinding ) { return false; } bool MakeFaceCommand::activated( int arg, Model * model ) { if ( arg == 0 && model ) { if ( model->getAnimationMode() == Model::ANIMMODE_NONE ) { std::list verts; model->getSelectedVertices( verts ); if ( verts.size() == 3 ) { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Face created" ).toUtf8().data() ); int v1, v2, v3; std::list::iterator it = verts.begin(); v1 = *it; it++; v2 = *it; it++; v3 = *it; int tri = model->addTriangle( v1, v2, v3 ); model->selectTriangle( tri ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "Must select exactly 3 vertices" ).toUtf8().data() ); } } } return false; } mm3d-1.3.15/src/commands/makefacecmd.h000066400000000000000000000024651466047437300174470ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MAKEFACECMD_H #define __MAKEFACECMD_H #include "command.h" class MakeFaceCommand : public Command { public: MakeFaceCommand(); virtual ~MakeFaceCommand(); int getCommandCount() { return 1; }; bool getKeyBinding( int arg, int & keyBinding ); const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; }; #endif // __MAKEFACECMD_H mm3d-1.3.15/src/commands/offsetcmd.cc000066400000000000000000000026121466047437300173310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "offsetcmd.h" #include "offsetwin.h" #include "log.h" #include #include OffsetCommand::OffsetCommand() { } OffsetCommand::~OffsetCommand() { } bool OffsetCommand::activated( int arg, Model * model ) { OffsetWin * win = new OffsetWin( model, NULL ); win->show(); return true; } const char * OffsetCommand::getPath() { return GEOM_VERTICES_MENU; } const char * OffsetCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Offset by Normal..." ); } mm3d-1.3.15/src/commands/offsetcmd.h000066400000000000000000000033521466047437300171750ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __OFFSETCMD_H #define __OFFSETCMD_H #include "command.h" #include class OffsetCommand : public Command { public: OffsetCommand(); virtual ~OffsetCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: void getConnected( Model * model, int vert, std::list & conList, std::list & triList); void addToList( std::list & l, int ignore, int val ); int createMissingTriangle( Model * model, unsigned int v, std::list & conList, std::list & triList ); int triangleCount( Model * model, unsigned int v1, unsigned int v2, std::list & triList, int & tri ); }; #endif // __CAPCMD_H mm3d-1.3.15/src/commands/pastecmd.cc000066400000000000000000000050211466047437300171540ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "pastecmd.h" #include "model.h" #include "filtermgr.h" #include "texmgr.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include "sysconf.h" #include "misc.h" #include "errorobj.h" #include #include #include #include using std::list; using std::map; PasteCommand::PasteCommand() { } PasteCommand::~PasteCommand() { } bool PasteCommand::activated( int arg, Model * model ) { if ( model ) { std::string clipfile = getMm3dHomeDirectory(); clipfile += "/clipboard"; mkpath( clipfile.c_str(), 0755 ); clipfile += "/clipboard.mm3d"; Model * m = new Model; Model::ModelErrorE err = FilterManager::getInstance()->readFile( m, clipfile.c_str() ); if ( err == Model::ERROR_NONE ) { model->mergeModels( m, true, Model::AM_NONE, false ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Paste complete" ).toUtf8().data() ); } else { if ( err == Model::ERROR_NO_FILE ) { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Nothing to paste" ).toUtf8().data() ); } else if ( Model::operationFailed( err ) ) { QString reason = modelErrStr( err, m ); msg_error( (const char *) qApp->translate( "Command", "Paste failed: %1\n%2" ).arg( reason ).arg( clipfile.c_str() ).toUtf8() ); } } delete m; return true; } else { return false; } } const char * PasteCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Paste from Clipboard" ); } mm3d-1.3.15/src/commands/pastecmd.h000066400000000000000000000025061466047437300170230ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __PASTECMD_H #define __PASTECMD_H #include "command.h" #include class PasteCommand : public Command { public: PasteCommand(); virtual ~PasteCommand(); int getCommandCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __PASTECMD_H mm3d-1.3.15/src/commands/rotatetexcmd.cc000066400000000000000000000104331466047437300200620ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "rotatetexcmd.h" #include "model.h" #include "msg.h" #include "modelstatus.h" #include "cmdmgr.h" #include "log.h" #include #include RotateTextureCommand::RotateTextureCommand() { } RotateTextureCommand::~RotateTextureCommand() { } const char * RotateTextureCommand::getPath() { return GEOM_FACES_MENU; } const char * RotateTextureCommand::getName( int arg ) { switch ( arg ) { case 0: return QT_TRANSLATE_NOOP( "Command", "Rotate Texture Coordinates" ); break; case 1: return QT_TRANSLATE_NOOP( "Command", "Face" ); break; case 2: return QT_TRANSLATE_NOOP( "Command", "Group" ); break; default: break; } return "[Out of range]"; } bool RotateTextureCommand::getKeyBinding( int arg, int & keyBinding ) { return false; } bool RotateTextureCommand::activated( int arg, Model * model ) { if ( arg == 1 && model ) { if ( model->getAnimationMode() == Model::ANIMMODE_NONE ) { std::list tris; model->getSelectedTriangles( tris ); if ( tris.size() > 0 ) { std::list::iterator it; float s, t; float oldS, oldT; for ( it = tris.begin(); it != tris.end(); it++ ) { model->getTextureCoords( *it, 2, oldS, oldT ); model->getTextureCoords( *it, 1, s, t ); model->setTextureCoords( *it, 2, s, t ); model->getTextureCoords( *it, 0, s, t ); model->setTextureCoords( *it, 1, s, t ); model->setTextureCoords( *it, 0, oldS, oldT ); } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Texture coordinates rotated" ).toUtf8().data() ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "Must select faces" ).toUtf8().data() ); } } } if ( arg == 2 && model ) { if ( model->getAnimationMode() == Model::ANIMMODE_NONE ) { std::list tris; model->getSelectedTriangles( tris ); if ( tris.size() > 0 ) { std::list::iterator it; float s; float t; float temp; for ( it = tris.begin(); it != tris.end(); it++ ) { model->getTextureCoords( *it, 0, s, t ); temp = s; s = 0.5 - (t - 0.5); t = temp; model->setTextureCoords( *it, 0, s, t ); model->getTextureCoords( *it, 1, s, t ); temp = s; s = 0.5 - (t - 0.5); t = temp; model->setTextureCoords( *it, 1, s, t ); model->getTextureCoords( *it, 2, s, t ); temp = s; s = 0.5 - (t - 0.5); t = temp; model->setTextureCoords( *it, 2, s, t ); } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Texture coordinates rotated" ).toUtf8().data() ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "Must select faces" ).toUtf8().data() ); } } } return false; } mm3d-1.3.15/src/commands/rotatetexcmd.h000066400000000000000000000025071466047437300177270ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ROTATETEXCMD_H #define __ROTATETEXCMD_H #include "command.h" class RotateTextureCommand : public Command { public: RotateTextureCommand(); virtual ~RotateTextureCommand(); int getCommandCount() { return 3; }; bool getKeyBinding( int arg, int & keyBinding ); const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; }; #endif // __ROTATETEXCMD_H mm3d-1.3.15/src/commands/selectfreecmd.cc000066400000000000000000000033441466047437300201670ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include #include #include #include "selectfreecmd.h" #include "model.h" #include "msg.h" #include "modelstatus.h" SelectFreeCommand::SelectFreeCommand() { } SelectFreeCommand::~SelectFreeCommand() { } const char * SelectFreeCommand::getPath() { return GEOM_VERTICES_MENU; } const char * SelectFreeCommand::getName( int arg ) { if ( arg == 0 ) { return QT_TRANSLATE_NOOP( "Command", "Select Free Vertices" ); } else { return "[Out of range]"; } } bool SelectFreeCommand::activated( int arg, Model * model ) { if ( model ) { model->selectFreeVertices(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Free-floating vertices selected" ).toUtf8().data() ); return true; } else { return false; } } mm3d-1.3.15/src/commands/selectfreecmd.h000066400000000000000000000024021466047437300200230ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTFREE_H #define __SELECTFREE_H #include "command.h" class SelectFreeCommand : public Command { public: SelectFreeCommand(); virtual ~SelectFreeCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; }; #endif // __SELECTFREE_H mm3d-1.3.15/src/commands/simplifycmd.cc000066400000000000000000000026401466047437300177000ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "simplifycmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include SimplifyMeshCommand::SimplifyMeshCommand() { } SimplifyMeshCommand::~SimplifyMeshCommand() { } bool SimplifyMeshCommand::activated( int arg, Model * model ) { model->simplifySelectedMesh(); return true; } const char * SimplifyMeshCommand::getPath() { return GEOM_MESHES_MENU; } const char * SimplifyMeshCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Simplify Mesh" ); } mm3d-1.3.15/src/commands/simplifycmd.h000066400000000000000000000024311466047437300175400ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SIMPLIFYCMD_H #define __SIMPLIFYCMD_H #include "command.h" class SimplifyMeshCommand : public Command { public: SimplifyMeshCommand(); virtual ~SimplifyMeshCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __SIMPLIFYCMD_H mm3d-1.3.15/src/commands/snapcmd.cc000066400000000000000000000142021466047437300170020ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2005 Johannes Kroll * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // Integrated into MM3D by Kevin Worcester // This code is modified from the snap_together plugin written by Johannes Kroll #include "menuconf.h" #include "snapcmd.h" #include "cmdmgr.h" #include "model.h" #include "pluginapi.h" #include "version.h" #include "log.h" #include "weld.h" #include #include #include #include #include #include SnapCommand::SnapCommand() { } SnapCommand::~SnapCommand() { } // set the coordinates of all the selected vertices to their arithmetic mean. static void snap_together(Model *model, const list & selection) { double coord[3]= { 0, 0, 0 }, coord_temp[3]; list::const_iterator iter; for(iter= selection.begin(); iter!=selection.end(); iter++) { model->getVertexCoords(*iter, coord_temp); coord[0]+= coord_temp[0]; coord[1]+= coord_temp[1]; coord[2]+= coord_temp[2]; } coord[0]/= selection.size(); coord[1]/= selection.size(); coord[2]/= selection.size(); for(iter= selection.begin(); iter!=selection.end(); iter++) { model->moveVertex(*iter, coord[0], coord[1], coord[2]); } } // snap 2 vertices together, delete one of them, and take care of affected triangles. static void snap_together_two(Model *model, int v0, int v1) { double coord_v0[3], coord_v1[3]; model->getVertexCoords(v0, coord_v0); model->getVertexCoords(v1, coord_v1); coord_v0[0]= (coord_v0[0]+coord_v1[0]) / 2; coord_v0[1]= (coord_v0[1]+coord_v1[1]) / 2; coord_v0[2]= (coord_v0[2]+coord_v1[2]) / 2; model->moveVertex(v0, coord_v0[0], coord_v0[1], coord_v0[2]); model->moveVertex(v1, coord_v0[0], coord_v0[1], coord_v0[2]); } // find the nearest vertex in the selection which is not contained in the exclude (already processed) list. #define DISTANCE(x, y, z) sqrt( (x)*(x) + (y)*(y) + (z)*(z) ) int find_nearest_vertex(Model *model, int vertex, const list & selection, const list & exclude) { double smallest_distance= 0xdeadbeef; int nearest_vertex= -1; double coord[3], coord_temp[3]; list::const_iterator iter; model->getVertexCoords(vertex, coord); for(iter= selection.begin(); iter!=selection.end(); iter++) { if(*iter!=vertex) { list::const_iterator it= exclude.begin(); while(it!=exclude.end() && *it != *iter) it++; if( *it != *iter ) { model->getVertexCoords( *iter, coord_temp ); double distance= DISTANCE(coord_temp[0]-coord[0], coord_temp[1]-coord[1], coord_temp[2]-coord[2]); if(distance < smallest_distance) { smallest_distance= distance; nearest_vertex= *iter; } } } } return nearest_vertex; } const char * SnapCommand::getPath() { return GEOM_VERTICES_MENU; } const char * SnapCommand::getName( int arg ) { switch ( arg ) { case 0: default: return QT_TRANSLATE_NOOP( "Command", "Snap Vertices Together" ); break; case 1: return QT_TRANSLATE_NOOP( "Command", "Snap All Selected" ); break; case 2: return QT_TRANSLATE_NOOP( "Command", "Snap Nearest Selected" ); break; case 3: return QT_TRANSLATE_NOOP( "Command", "Snap All and Weld" ); break; case 4: return QT_TRANSLATE_NOOP( "Command", "Snap Nearest and Weld" ); break; } return ""; } bool SnapCommand::activated( int arg, Model * model ) { list selection; model->getSelectedVertices( selection ); if ( selection.size() >= 2 ) { if ( arg == 1 || arg == 3 ) { snap_together(model, selection); } else { list excludelist; list::iterator sel_iter; for ( sel_iter = selection.begin(); sel_iter != selection.end(); sel_iter++ ) { list::iterator it = excludelist.begin(); while ( it != excludelist.end() && *it != *sel_iter ) { it++; } if ( *it != *sel_iter ) { int nearest_vertex= find_nearest_vertex(model, *sel_iter, selection, excludelist); if ( nearest_vertex != -1 ) { snap_together_two( model, *sel_iter, nearest_vertex ); excludelist.push_back(*sel_iter); excludelist.push_back(nearest_vertex); } } } } if ( arg == 3 || arg == 4 ) { weldSelectedVertices( model ); } model->deleteOrphanedVertices(); return true; } else { return false; } } #ifdef PLUGIN //------------------------------------------------------------------ // Plugin functions //------------------------------------------------------------------ PLUGIN_API bool plugin_init() { CommandManager::getInstance()->registerCommand( new SnapCommand() ); log_debug( "Snap Together Plugin Initialized\n" ); return true; } // We have no cleanup to do PLUGIN_API bool plugin_uninit() { return true; } PLUGIN_API const char * plugin_mm3d_version() { return VERSION_STRING; } PLUGIN_API const char * plugin_version() { return "1.0.0"; } PLUGIN_API const char * plugin_desc() { return "Snap Vertices Together"; } #endif // PLUGIN mm3d-1.3.15/src/commands/snapcmd.h000066400000000000000000000041541466047437300166510ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2005 Johannes Kroll * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SNAPCMD_H #define __SNAPCMD_H #include "command.h" class SnapCommand: public Command { public: SnapCommand(); virtual ~SnapCommand(); int getCommandCount() { return 5; } const char * getPath(); const char * getName( int arg ); // If you want a keybinding for your command you must set keyBinding // and return true. The value of keyBinding is a key code as defined // in qnamespace.h (part of the Qt library). bool getKeyBinding( int arg, int & keyBinding ) { return false; }; // activated means that the user wants to run the command specified in arg // on the specified model. // // A return value of 'true' means that the model was modified by the // command. 'false' means the model was unmodified. An unmodified // model may be the result of a canceled dialog box, or an empty // selection set. bool activated( int arg, Model * model ); // The following functions determine how the tools are grouped in // menus. // Does this command operate on primitives (vertices, triangles, joints)? bool isPrimitive() { return true; }; // Does this command operate on groups of polygons? bool isGroup() { return false; }; }; #endif // __SNAPCMD_H mm3d-1.3.15/src/commands/spherifycmd.cc000066400000000000000000000026261466047437300177010ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "spherifycmd.h" #include "spherifywin.h" #include "log.h" #include #include SpherifyCommand::SpherifyCommand() { } SpherifyCommand::~SpherifyCommand() { } bool SpherifyCommand::activated( int arg, Model * model ) { SpherifyWin * win = new SpherifyWin( model, NULL ); win->show(); return true; } const char * SpherifyCommand::getPath() { return GEOM_MESHES_MENU; } const char * SpherifyCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Spherify..." ); } mm3d-1.3.15/src/commands/spherifycmd.h000066400000000000000000000025661466047437300175460ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SPHERIFYCMD_H #define __SPHERIFYCMD_H #include "command.h" #include class SpherifyCommand : public Command { public: SpherifyCommand(); virtual ~SpherifyCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool getKeyBinding( int arg, int & keyBinding ) { return false; }; bool isPrimitive() { return true; }; protected: }; #endif // __SPHERIFYCMD_H mm3d-1.3.15/src/commands/subdividecmd.cc000066400000000000000000000034231466047437300200220ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "subdividecmd.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include #include SubdivideCommand::SubdivideCommand() { } SubdivideCommand::~SubdivideCommand() { } bool SubdivideCommand::activated( int arg, Model * model ) { if ( model->getSelectedTriangleCount() >= 1 ) { model->subdivideSelectedTriangles(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Command", "Subdivide complete" ).toUtf8().data() ); return true; } model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have at least 1 face selected to Subdivide Faces" ).toUtf8().data() ); return false; } const char * SubdivideCommand::getPath() { return GEOM_FACES_MENU; } const char * SubdivideCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Subdivide Faces" ); } mm3d-1.3.15/src/commands/subdividecmd.h000066400000000000000000000024041466047437300176620ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SUBDIVIDECMD_H #define __SUBDIVIDECMD_H #include "command.h" class SubdivideCommand : public Command { public: SubdivideCommand(); virtual ~SubdivideCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; }; #endif // __SUBDIVIDECMD_H mm3d-1.3.15/src/commands/unweldcmd.cc000066400000000000000000000041041466047437300173370ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "unweldcmd.h" #include "model.h" #include "msg.h" #include "log.h" #include "modelstatus.h" #include "weld.h" // does the real work #include #include #include using std::list; UnweldCommand::UnweldCommand() { } UnweldCommand::~UnweldCommand() { } bool UnweldCommand::activated( int arg, Model * model ) { if ( model ) { std::list vert; model->getSelectedVertices( vert ); if ( !vert.empty() ) { int unwelded = 0; int welded = 0; unweldSelectedVertices( model, unwelded, welded ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", (qApp->translate( "Command", "Unwelded %1 vertices into %2 vertices" ).arg(welded).arg(unwelded)).toUtf8().data() ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have 1 or more vertices selected to unweld." ).toUtf8().data() ); } } return false; } const char * UnweldCommand::getPath() { return GEOM_VERTICES_MENU; } const char * UnweldCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Unweld Vertices" ); } mm3d-1.3.15/src/commands/unweldcmd.h000066400000000000000000000026261466047437300172100ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __UNWELDCMD_H #define __UNWELDCMD_H #include "command.h" #include class UnweldCommand : public Command { public: UnweldCommand(); virtual ~UnweldCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); //bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::SHIFT+Qt::CTRL+Qt::Key_W; return true; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __UNWELDCMD_H mm3d-1.3.15/src/commands/weldcmd.cc000066400000000000000000000041201466047437300167720ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "weldcmd.h" #include "model.h" #include "glmath.h" #include "log.h" #include "msg.h" #include "modelstatus.h" #include "weld.h" #include #include #include #include using std::list; using std::map; WeldCommand::WeldCommand() { } WeldCommand::~WeldCommand() { } bool WeldCommand::activated( int arg, Model * model ) { if ( model ) { list vert; model->getSelectedVertices( vert ); if ( vert.size() > 1 ) { int unwelded = 0; int welded = 0; weldSelectedVertices( model, 0.0001, unwelded, welded ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", (qApp->translate( "Command", "Welded %1 vertices into %2 vertices" ).arg(unwelded).arg(welded)).toUtf8().data() ); return true; } else { model_status( model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Command", "You must have 2 or more vertices selected to weld." ).toUtf8().data() ); } } return false; } const char * WeldCommand::getPath() { return GEOM_VERTICES_MENU; } const char * WeldCommand::getName( int arg ) { return QT_TRANSLATE_NOOP( "Command", "Weld Vertices" ); } mm3d-1.3.15/src/commands/weldcmd.h000066400000000000000000000025761466047437300166510ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __WELDCMD_H #define __WELDCMD_H #include "command.h" #include class WeldCommand : public Command { public: WeldCommand(); virtual ~WeldCommand(); int getCommandCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::CTRL+Qt::Key_W; return true; }; bool activated( int arg, Model * model ); bool isPrimitive() { return true; }; protected: }; #endif // __WELDCMD_H mm3d-1.3.15/src/depui/000077500000000000000000000000001466047437300143545ustar00rootroot00000000000000mm3d-1.3.15/src/depui/Makefile.am000066400000000000000000000011671466047437300164150ustar00rootroot00000000000000noinst_LIBRARIES = libdepui.a libdepui_HFILES = \ errorobj.h \ modelviewport.h \ textureframe.h \ texwidget.h libdepui_MOC = \ errorobj.moc.cc \ modelviewport.moc.cc \ textureframe.moc.cc \ texwidget.moc.cc libdepui_a_SOURCES = \ errorobj.cc \ modelviewport.cc \ textureframe.cc \ texwidget.cc \ $(libdepui_MOC) \ $(libdepui_HFILES) %.moc.cc: %.h $(QT_MOC) -o $@ $< AM_CPPFLAGS = $(CORE_PROFILE) $(COVFLAGS) -Wall -I$(srcdir)/../libmm3d -I$(srcdir)/../mm3dcore -I$(srcdir)/../ -DMM3D_EDIT $(all_includes) $(QT_CXXFLAGS) $(LUALIB_CCFLAGS) $(GL_CFLAGS) CLEANFILES = $(libdepui_MOC) *.gcno *.gcda mm3d-1.3.15/src/depui/errorobj.cc000066400000000000000000000102611466047437300165070ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "errorobj.h" ErrorObject g_errorObject; ErrorObject::ErrorObject( QObject * parent ) : QObject( parent ) { } ErrorObject::~ErrorObject() { } QString ErrorObject::getModelErrorString( Model::ModelErrorE err, Model * model ) { switch ( err ) { case Model::ERROR_NONE: return tr("Success"); case Model::ERROR_CANCEL: return tr("Canceled"); case Model::ERROR_UNKNOWN_TYPE: return tr("Unrecognized file extension (unknown type)"); case Model::ERROR_UNSUPPORTED_OPERATION: return tr("Operation not supported for this file type"); case Model::ERROR_BAD_ARGUMENT: return tr("Invalid argument (internal error)"); case Model::ERROR_NO_FILE: return tr("File does not exist"); case Model::ERROR_NO_ACCESS: return tr("Permission denied"); case Model::ERROR_FILE_OPEN: return tr("Could not open file"); case Model::ERROR_FILE_READ: return tr("Could not read from file"); case Model::ERROR_BAD_MAGIC: return tr("File is the wrong type or corrupted"); case Model::ERROR_UNSUPPORTED_VERSION: return tr("Unsupported version"); case Model::ERROR_BAD_DATA: return tr("File contains invalid data"); case Model::ERROR_UNEXPECTED_EOF: return tr("Unexpected end of file"); case Model::ERROR_EXPORT_ONLY: return tr("Write not supported, try \"Export...\""); case Model::ERROR_FILTER_SPECIFIC: if ( model ) { return QString::fromUtf8( model->getFilterSpecificError() ); } else { return QString::fromUtf8( Model::getLastFilterSpecificError() ); } case Model::ERROR_UNKNOWN: return tr("Unknown error"); default: break; } return QString("FIXME: Untranslated model error"); } QString ErrorObject::getTextureErrorString( Texture::ErrorE err ) { switch ( err ) { case Texture::ERROR_NONE: return tr("Success"); case Texture::ERROR_NO_FILE: return tr("File does not exist"); case Texture::ERROR_NO_ACCESS: return tr("Permission denied"); case Texture::ERROR_FILE_OPEN: return tr("Could not open file"); case Texture::ERROR_FILE_READ: return tr("Could not read from file"); case Texture::ERROR_FILE_WRITE: return tr("Could not write file"); case Texture::ERROR_BAD_MAGIC: return tr("File is the wrong type or corrupted"); case Texture::ERROR_UNSUPPORTED_VERSION: return tr("Unsupported version"); case Texture::ERROR_BAD_DATA: return tr("File contains invalid data"); case Texture::ERROR_UNEXPECTED_EOF: return tr("Unexpected end of file"); case Texture::ERROR_UNSUPPORTED_OPERATION: return tr("This operation is not supported"); case Texture::ERROR_BAD_ARGUMENT: return tr("Invalid argument (internal error)"); case Texture::ERROR_UNKNOWN: return tr("Unknown error"); default: break; } return QString("FIXME: Untranslated texture error"); } QString modelErrStr( Model::ModelErrorE err, Model * model ) { return g_errorObject.getModelErrorString( err, model ); } QString textureErrStr( Texture::ErrorE err ) { return g_errorObject.getTextureErrorString( err ); } mm3d-1.3.15/src/depui/errorobj.h000066400000000000000000000027141466047437300163550ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ERROROBJ_H #define __ERROROBJ_H #include #include #include "model.h" #include "texture.h" class ErrorObject : public QObject { Q_OBJECT public: ErrorObject( QObject * parent = NULL ); virtual ~ErrorObject( ); QString getModelErrorString( Model::ModelErrorE err, Model * model = NULL ); QString getTextureErrorString( Texture::ErrorE err ); }; extern ErrorObject g_errorObject; // Convenience functions QString modelErrStr( Model::ModelErrorE err, Model * model = NULL ); QString textureErrStr( Texture::ErrorE err ); #endif // __ERROROBJ_H mm3d-1.3.15/src/depui/modelviewport.cc000066400000000000000000002445011466047437300175710ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "../implui/qtmain.h" #include "model.h" #include "toolbox.h" #include "tool.h" #include "glmath.h" #include "decalmgr.h" #include "decal.h" #include "log.h" #include "modelstatus.h" #include "texmgr.h" #include "modelviewport.h" #include "3dmprefs.h" #include "mm3dport.h" #include "pixmap/arrow.xpm" #include "pixmap/crosshairrow.xpm" #include #include #include #include #include #include #include #include #include #include #define NEWVIEWPORT #define VP_ZOOMSCALE 0.75 #define MM3D_ENABLEALPHA static int const SCROLL_SIZE = 16; struct _ScrollButton_t { int x; int y; int texIndex; float s1; float t1; float s2; float t2; float s3; float t3; float s4; float t4; }; typedef struct _ScrollButton_t ScrollButtonT; static ScrollButtonT s_buttons[ ModelViewport::ScrollButtonMAX ] = { { -18, -18, 1, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }, // Pan { -52, -18, 0, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }, // Left { -35, -18, 0, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f }, // Right { -18, -35, 0, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }, // Up { -18, -52, 0, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }, // Down }; static Matrix s_mat; static bool useDarkBackground( void ) { int color_scheme = g_prefs( "ui_colorscheme_type" ).intValue(); return ( color_scheme == 2 || ( color_scheme == 0 && ui_getdarkmode() ) ); } ModelViewport::ModelViewport( QWidget * parent ) : QOpenGLWidget( parent ), m_model( NULL ), m_operation( MO_None ), m_activeButton( Qt::NoButton ), m_viewDirection( ViewFront ), m_centerX( 0.0 ), m_centerY( 0.0 ), m_centerZ( 0.0 ), m_rotX( 0.0 ), m_rotY( 0.0 ), m_rotZ( 0.0 ), m_zoomLevel( 32.0 ), m_unitWidth( 1.0 ), m_far( 10000.0 ), m_near( 1.0 ), m_farOrtho( 1000000.0 ), m_nearOrtho( 0.001 ), m_viewportWidth( 0 ), m_viewportHeight( 0 ), m_scrollTimer( new QTimer() ), m_overlayButton( ScrollButtonMAX ), m_capture( false ), m_texturesLoaded( false ), m_toolbox( NULL ) { // Default preferences m_arcballPoint[0] = 0.0; m_arcballPoint[1] = 0.0; m_arcballPoint[2] = 0.0; m_scrollTimer->setSingleShot( true ); g_prefs.setDefault( "ui_colorscheme_type", 0 ); g_prefs.setDefault( "ui_grid_inc", 4.0 ); g_prefs.setDefault( "ui_3dgrid_inc", 4.0 ); g_prefs.setDefault( "ui_3dgrid_count", 6 ); g_prefs.setDefault( "ui_3dgrid_xy", 0 ); g_prefs.setDefault( "ui_3dgrid_xz", 1 ); g_prefs.setDefault( "ui_3dgrid_yz", 0 ); setFocusPolicy( Qt::WheelFocus ); setMinimumSize( 220, 180 ); double rot[3] = { 45 * PIOVER180, 0, 0 }; s_mat.setRotation( rot ); if ( useDarkBackground() ) { m_backColor.setRgb( 130 / 2, 200 / 2, 200 / 2 ); } else { m_backColor.setRgb( 130, 200, 200 ); } //setAcceptDrops( true ); // Drag and drop is not implemented, see ModelViewport::dragEnterEvent. setMouseTracking( true ); connect( m_scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimeout())); setCursor( Qt::ArrowCursor ); } ModelViewport::~ModelViewport() { log_debug( "deleting model viewport\n" ); if ( isValid() ) { makeCurrent(); glDeleteTextures( 1, &m_backgroundTexture ); glDeleteTextures( 2, m_scrollTextures ); } freeTextures(); delete m_scrollTimer; } void ModelViewport::freeTextures() { log_debug( "freeing texture for viewport\n" ); makeCurrent(); if ( m_model ) { m_model->removeContext( static_cast( this ) ); } } void ModelViewport::initializeGL() { if ( !isValid() ) { log_error( "model viewport does not have a valid OpenGL context\n" ); return; } QPixmap arrow( arrow_xpm ); QPixmap cross( crosshairrow_xpm ); QImage img; glGenTextures( 2, m_scrollTextures ); img = arrow.toImage(); makeTextureFromImage( img, m_scrollTextures[0] ); img = cross.toImage(); makeTextureFromImage( img, m_scrollTextures[1] ); glShadeModel( GL_SMOOTH ); glClearColor( m_backColor.red() / 256.0, m_backColor.green() / 256.0, m_backColor.blue() / 256.0, 1.0f ); glClearDepth( 1.0f ); glEnable( GL_DEPTH_TEST ); glDepthFunc( GL_LEQUAL ); glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); { GLfloat ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat diffuse[] = { 0.9f, 0.9f, 0.9f, 1.0f }; GLfloat position[] = { 0.0f, 0.0f, 1.0f, 0.0f }; glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE ); glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); glLightfv( GL_LIGHT0, GL_POSITION, position ); //glEnable( GL_LIGHT0 ); } { GLfloat ambient[] = { 0.8f, 0.4f, 0.4f, 1.0f }; GLfloat diffuse[] = { 0.9f, 0.5f, 0.5f, 1.0f }; GLfloat position[] = { 0.0f, 0.0f, 1.0f, 0.0f }; glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE ); glLightfv( GL_LIGHT1, GL_AMBIENT, ambient ); glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse ); glLightfv( GL_LIGHT1, GL_POSITION, position ); //glEnable( GL_LIGHT1 ); } { GLint texSize = 0; glGetIntegerv( GL_MAX_TEXTURE_SIZE, &texSize ); log_debug( "max texture size is %dx%d\n", texSize, texSize ); } glGenTextures( 1, &m_backgroundTexture ); checkGlErrors(); #ifdef MM3D_ENABLEALPHA glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if ( ! glIsEnabled( GL_BLEND) ) { log_warning( "alpha not supported\n" ); glDisable( GL_BLEND ); glGetError(); // clear errors } #endif // MM3D_ENABLEALPHA } void ModelViewport::resizeGL( int w, int h ) { if ( h == 0 ) { h = 1; } // FIXME?: I think Qt 5.6 (or later) changed resizeGL() behavior from // always being pixels to points that need to be scaled but I don't // have convient access to test old Qt versions. So using w and h or // scaling them should potentially have a Qt version check. m_viewportWidth = w * devicePixelRatioF(); m_viewportHeight = h * devicePixelRatioF(); adjustViewport(); } void ModelViewport::paintGL() { if ( !isValid() ) return; //LOG_PROFILE(); if ( m_inOverlay ) { setViewportDraw(); } if ( m_capture ) { glClearColor( 130.0 / 256.0, 200.0 / 256.0, 200.0 / 256.0, 1.0f ); } float viewPoint[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; if ( m_viewDirection == ViewPerspective || m_viewDirection == ViewOrtho ) { glEnable( GL_LIGHT0 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); //if ( m_viewDirection == ViewPerspective ) { glLoadIdentity( ); } #ifdef NEWVIEWPORT viewPoint[0] = m_arcballPoint[0]; // m_centerX; viewPoint[1] = m_arcballPoint[1]; // m_centerY; viewPoint[2] = m_arcballPoint[2] + (m_zoomLevel * 2.0); if ( m_viewDirection == ViewPerspective ) { glTranslatef( -viewPoint[0], -viewPoint[1], -viewPoint[2] ); } else { glTranslatef( 0.0, 0.0, -m_farOrtho / 2.0 ); } glRotatef( m_rotZ, 0.0, 0.0, 1.0); glRotatef( m_rotY, 0.0, 1.0, 0.0); glRotatef( m_rotX, 1.0, 0.0, 0.0); #else viewPoint[0] = m_centerX; viewPoint[1] = m_centerY; viewPoint[2] = (m_zoomLevel * 2.0); glTranslatef( -viewPoint[0], -viewPoint[1], -viewPoint[2] ); glRotatef( m_rotZ, 0.0, 0.0, 1.0); glRotatef( m_rotY, 0.0, 1.0, 0.0); glRotatef( m_rotX, 1.0, 0.0, 0.0); #endif // NEWVIEWPORT Matrix m; m.setRotationInDegrees( 0.0f, 0.0f, -m_rotZ ); m.apply( viewPoint ); m.setRotationInDegrees( 0.0f, -m_rotY, 0.0f ); m.apply( viewPoint ); m.setRotationInDegrees( -m_rotX, 0.0f, 0.0f ); m.apply( viewPoint ); } else { glDisable( GL_LIGHT0 ); glDisable( GL_LIGHT1 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity( ); viewPoint[0] = 0.0f; viewPoint[1] = 0.0f; viewPoint[2] = 500000.0f; glTranslatef( -viewPoint[0], -viewPoint[1], -viewPoint[2] ); Matrix m; switch ( m_viewDirection ) { case ViewFront: // do nothing break; case ViewBack: glRotatef( 180.0, 0.0, 1.0, 0.0); m.setRotationInDegrees( 0.0f, -180.0f, 0.0f ); break; case ViewLeft: glRotatef( -90.0, 0.0, 1.0, 0.0); m.setRotationInDegrees( 0.0f, 90.0f, 0.0f ); break; case ViewRight: glRotatef( 90.0, 0.0, 1.0, 0.0); m.setRotationInDegrees( 0.0f, -90.0f, 0.0f ); break; case ViewTop: glRotatef( 90.0, 1.0, 0.0, 0.0); m.setRotationInDegrees( -90.0f, 0.0f, 0.0f ); break; case ViewBottom: glRotatef( -90.0, 1.0, 0.0, 0.0); m.setRotationInDegrees( 90.0f, 0.0f, 0.0f ); break; default: log_error( "Unknown ViewDirection: %d\n", m_viewDirection ); return; } m.apply( viewPoint ); } if ( !m_capture ) { drawGridLines(); } if ( m_model ) { glColor3f( 0.7, 0.7, 0.7 ); if ( m_viewDirection == ViewPerspective ) { glEnable( GL_LIGHTING ); int opt = Model::DO_TEXTURE | Model::DO_SMOOTHING | ( (g_prefs( "ui_render_bad_textures" ).intValue() == 0) ? 0 : Model::DO_BADTEX); bool drawSelections = (g_prefs( "ui_render_3d_selections" ).intValue() == 0) ? false : true; switch ( m_model->getPerspectiveDrawMode() ) { case ViewWireframe: opt = Model::DO_WIREFRAME; drawSelections = true; break; case ViewFlat: opt = Model::DO_NONE; break; case ViewSmooth: opt = Model::DO_SMOOTHING; break; case ViewAlpha: opt = opt | Model::DO_ALPHA; break; default: break; } opt |= ( (g_prefs( "ui_render_backface_cull" ).intValue() == 0) ? 0 : Model::DO_BACKFACECULL); if ( opt != Model::DO_WIREFRAME ) { glEnable( GL_LIGHTING ); if ( drawSelections ) { glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1, 0 ); } m_model->draw( opt, static_cast( this) , viewPoint ); if ( drawSelections ) { glDisable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 0, 0 ); } } if ( drawSelections ) { glDisable( GL_LIGHTING ); m_model->drawLines( devicePixelRatioF() ); m_model->drawVertices( devicePixelRatioF() ); glDisable( GL_DEPTH_TEST ); m_model->drawJoints( devicePixelRatioF() ); } glDisable( GL_LIGHTING ); glDisable( GL_DEPTH_TEST ); m_model->drawPoints( devicePixelRatioF() ); m_model->drawProjections( devicePixelRatioF() ); } else { // Draw background drawBackground(); glClear( GL_DEPTH_BUFFER_BIT ); int drawMode = m_model->getCanvasDrawMode(); if ( drawMode != ViewWireframe ) { glEnable( GL_LIGHTING ); glEnable( GL_LIGHT0 ); glEnable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 1, 0 ); int opt = Model::DO_TEXTURE | Model::DO_SMOOTHING | ( (g_prefs( "ui_render_bad_textures" ).intValue() == 0) ? 0 : Model::DO_BADTEX); switch ( drawMode ) { case ViewFlat: opt = Model::DO_NONE; break; case ViewSmooth: opt = Model::DO_SMOOTHING; break; case ViewAlpha: opt = opt | Model::DO_ALPHA; break; default: break; } opt |= ( (g_prefs( "ui_render_backface_cull" ).intValue() == 0) ? 0 : Model::DO_BACKFACECULL); m_model->draw( opt, static_cast( this ), viewPoint ); glDisable( GL_LIGHTING ); glDisable( GL_POLYGON_OFFSET_FILL ); glPolygonOffset( 0, 0 ); } m_model->drawLines( devicePixelRatioF() ); m_model->drawVertices( devicePixelRatioF() ); glDisable( GL_DEPTH_TEST ); m_model->drawJoints( devicePixelRatioF() ); m_model->drawPoints( devicePixelRatioF() ); m_model->drawProjections( devicePixelRatioF() ); for ( DecalList::iterator it = m_decals.begin(); it != m_decals.end(); it++ ) { (*it)->draw( devicePixelRatioF() ); } glEnable( GL_DEPTH_TEST ); } } glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); if ( !m_capture ) { drawOrigin(); } else { glEnable( GL_DEPTH_TEST ); } if ( this->hasFocus() ) { drawOverlay(); } checkGlErrors(); } void ModelViewport::drawGridLines() { if ( m_viewDirection == ViewPerspective || m_viewDirection == ViewOrtho ) { double inc = g_prefs( "ui_3dgrid_inc" ).doubleValue(); double max = g_prefs( "ui_3dgrid_count" ).doubleValue() * inc; double x; double y; double z; glLineWidth( devicePixelRatioF() ); glColor3f( 0.55f, 0.55f, 0.55f ); glBegin( GL_LINES ); if ( g_prefs( "ui_3dgrid_xy" ).intValue() != 0 ) { for ( x = -max; x <= max; x += inc ) { glVertex3f( x, -max, 0 ); glVertex3f( x, +max, 0 ); } for ( y = -max; y <= max; y += inc ) { glVertex3f( -max, y, 0 ); glVertex3f( +max, y, 0 ); } } if ( g_prefs( "ui_3dgrid_xz" ).intValue() != 0 ) { for ( x = -max; x <= max; x += inc ) { glVertex3f( x, 0, -max ); glVertex3f( x, 0, +max ); } for ( z = -max; z <= max; z += inc ) { glVertex3f( -max, 0, z ); glVertex3f( +max, 0, z ); } } if ( g_prefs( "ui_3dgrid_yz" ).intValue() != 0 ) { for ( y = -max; y <= max; y += inc ) { glVertex3f( 0, y, -max ); glVertex3f( 0, y, +max ); } for ( z = -max; z <= max; z += inc ) { glVertex3f( 0, -max, z ); glVertex3f( 0, +max, z ); } } glEnd(); } else { m_unitWidth = getUnitWidth(); double xRangeMin = 0; double xRangeMax = 0; double yRangeMin = 0; double yRangeMax = 0; double xStart = 0; double yStart = 0; double x = 0; double y = 0; glLineWidth( devicePixelRatioF() ); glColor3f( 0.55f, 0.55f, 0.55f ); glBegin( GL_LINES ); switch ( m_viewDirection ) { case ViewFront: xRangeMin = m_arcballPoint[0] - (m_width / 2.0); xRangeMax = m_arcballPoint[0] + (m_width / 2.0); yRangeMin = m_arcballPoint[1] - (m_height / 2.0); yRangeMax = m_arcballPoint[1] + (m_height / 2.0); xStart = m_unitWidth * ((int) (xRangeMin / m_unitWidth)); yStart = m_unitWidth * ((int) (yRangeMin / m_unitWidth)); for ( x = xStart; x < xRangeMax; x += m_unitWidth ) { glVertex3f( x, yRangeMin, 0 ); glVertex3f( x, yRangeMax, 0 ); } for ( y = yStart; y < yRangeMax; y += m_unitWidth ) { glVertex3f( xRangeMin, y, 0 ); glVertex3f( xRangeMax, y, 0 ); } break; case ViewBack: xRangeMin = -m_arcballPoint[0] - (m_width / 2.0); xRangeMax = -m_arcballPoint[0] + (m_width / 2.0); yRangeMin = m_arcballPoint[1] - (m_height / 2.0); yRangeMax = m_arcballPoint[1] + (m_height / 2.0); xStart = m_unitWidth * ((int) (xRangeMin / m_unitWidth)); yStart = m_unitWidth * ((int) (yRangeMin / m_unitWidth)); for ( x = xStart; x < xRangeMax; x += m_unitWidth ) { glVertex3f( x, yRangeMin, 0 ); glVertex3f( x, yRangeMax, 0 ); } for ( y = yStart; y < yRangeMax; y += m_unitWidth ) { glVertex3f( xRangeMin, y, 0 ); glVertex3f( xRangeMax, y, 0 ); } break; case ViewLeft: xRangeMin = -m_arcballPoint[0] - (m_width / 2.0); xRangeMax = -m_arcballPoint[0] + (m_width / 2.0); yRangeMin = m_arcballPoint[1] - (m_height / 2.0); yRangeMax = m_arcballPoint[1] + (m_height / 2.0); xStart = m_unitWidth * ((int) (xRangeMin / m_unitWidth)); yStart = m_unitWidth * ((int) (yRangeMin / m_unitWidth)); for ( x = xStart; x < xRangeMax; x += m_unitWidth ) { glVertex3f( 0, yRangeMin, x ); glVertex3f( 0, yRangeMax, x ); } for ( y = yStart; y < yRangeMax; y += m_unitWidth ) { glVertex3f( 0, y, xRangeMin ); glVertex3f( 0, y, xRangeMax ); } break; case ViewRight: xRangeMin = m_arcballPoint[0] - (m_width / 2.0); xRangeMax = m_arcballPoint[0] + (m_width / 2.0); yRangeMin = m_arcballPoint[1] - (m_height / 2.0); yRangeMax = m_arcballPoint[1] + (m_height / 2.0); xStart = m_unitWidth * ((int) (xRangeMin / m_unitWidth)); yStart = m_unitWidth * ((int) (yRangeMin / m_unitWidth)); for ( x = xStart; x < xRangeMax; x += m_unitWidth ) { glVertex3f( 0, yRangeMin, x ); glVertex3f( 0, yRangeMax, x ); } for ( y = yStart; y < yRangeMax; y += m_unitWidth ) { glVertex3f( 0, y, xRangeMin ); glVertex3f( 0, y, xRangeMax ); } break; case ViewTop: xRangeMin = m_arcballPoint[0] - (m_width / 2.0); xRangeMax = m_arcballPoint[0] + (m_width / 2.0); yRangeMin = -m_arcballPoint[1] - (m_height / 2.0); yRangeMax = -m_arcballPoint[1] + (m_height / 2.0); xStart = m_unitWidth * ((int) (xRangeMin / m_unitWidth)); yStart = m_unitWidth * ((int) (yRangeMin / m_unitWidth)); for ( x = xStart; x < xRangeMax; x += m_unitWidth ) { glVertex3f( x, 0, yRangeMin ); glVertex3f( x, 0, yRangeMax ); } for ( y = yStart; y < yRangeMax; y += m_unitWidth ) { glVertex3f( xRangeMin, 0, y ); glVertex3f( xRangeMax, 0, y ); } break; case ViewBottom: xRangeMin = m_arcballPoint[0] - (m_width / 2.0); xRangeMax = m_arcballPoint[0] + (m_width / 2.0); yRangeMin = m_arcballPoint[1] - (m_height / 2.0); yRangeMax = m_arcballPoint[1] + (m_height / 2.0); xStart = m_unitWidth * ((int) (xRangeMin / m_unitWidth)); yStart = m_unitWidth * ((int) (yRangeMin / m_unitWidth)); for ( x = xStart; x < xRangeMax; x += m_unitWidth ) { glVertex3f( x, 0, yRangeMin ); glVertex3f( x, 0, yRangeMax ); } for ( y = yStart; y < yRangeMax; y += m_unitWidth ) { glVertex3f( xRangeMin, 0, y ); glVertex3f( xRangeMax, 0, y ); } break; default: log_error( "Unhandled view direction: %d\n", m_viewDirection ); break; } glEnd(); glColor3f( 0.35f, 0.35f, 0.35f ); glRasterPos3f( xRangeMin, yRangeMin, 0.0f ); switch ( m_viewDirection ) { case ViewFront: break; case ViewBack: glRasterPos3f( xRangeMax, yRangeMin, 0.0f ); break; case ViewLeft: glRasterPos3f( 0.0f, yRangeMin, xRangeMax ); break; case ViewRight: glRasterPos3f( 0.0f, yRangeMin, xRangeMin ); break; case ViewTop: glRasterPos3f( xRangeMin, 0.0f, yRangeMax ); break; case ViewBottom: glRasterPos3f( xRangeMin, 0.0f, yRangeMin ); break; default: log_error( "Unhandled view direction: %d\n", m_viewDirection ); break; } #if 0 // FIXME: renderText() exists in QGLWidget but not QOpenGLWidget g_prefs.setDefault( "ui_render_text", 0 ); if ( g_prefs( "ui_render_text" ).intValue() != 0 ) { // Broken with Qt4 + non-accelerated nVidia card (possibly other configs as // well). That is why it is disabled by default. You can set ui_render_text // to a non-zero value in your mm3drc file to enable text rendering. QString text; text.sprintf( "%g", m_unitWidth ); renderText( 2, this->height() - 12, text, QFont( "Sans", 10 ) ); } #endif } } void ModelViewport::drawOrigin() { glDisable( GL_DEPTH_TEST ); glLineWidth( devicePixelRatioF() ); glBegin( GL_LINES ); double scale = m_zoomLevel / 10.0; #ifdef NEWVIEWPORT if ( m_viewDirection == ViewPerspective ) { double x = m_arcballPoint[0]; double y = m_arcballPoint[1]; double z = m_arcballPoint[2] + m_zoomLevel; scale = sqrt( x*x + y*y + z*z ) / 10.0; } #endif // NEWVIEWPORT glColor3f( 1, 0, 0 ); glVertex3f( 0, 0, 0 ); glVertex3f( scale, 0, 0 ); glColor3f( 0, 1, 0 ); glVertex3f( 0, 0, 0 ); glVertex3f( 0, scale, 0 ); glColor3f( 0, 0, 1 ); glVertex3f( 0, 0, 0 ); glVertex3f( 0, 0, scale ); glEnd(); glEnable( GL_DEPTH_TEST ); } void ModelViewport::drawBackground() { glDisable( GL_LIGHTING ); glColor3f( 1.0f, 1.0f, 1.0f ); updateBackground(); if ( m_backgroundFile[0] != '\0' ) { if ( m_viewDirection != ViewPerspective && m_viewDirection != ViewOrtho ) { int index = (int) m_viewDirection - 1; float cenX = 0.0f; float cenY = 0.0f; float cenZ = 0.0f; float minX = 0.0f; float minY = 0.0f; float minZ = 0.0f; float maxX = 0.0f; float maxY = 0.0f; float maxZ = 0.0f; float normX = 0.0f; float normY = 0.0f; float normZ = 0.0f; float w = m_texture->m_origWidth; float h = m_texture->m_origHeight; float dimMax = w > h ? w : h; float scale = m_model->getBackgroundScale( index ); m_model->getBackgroundCenter( index, cenX, cenY, cenZ ); glBindTexture( GL_TEXTURE_2D, m_backgroundTexture ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glEnable( GL_TEXTURE_2D ); glEnable( GL_BLEND ); glBegin( GL_QUADS ); switch ( m_viewDirection ) { case ViewFront: minZ = maxZ = -((m_farOrtho / 2.0f) - 0.1); minX = -scale * (w / dimMax) + cenX; maxX = scale * (w / dimMax) + cenX; minY = -scale * (h / dimMax) + cenY; maxY = scale * (h / dimMax) + cenY; normZ = 1.0f; break; case ViewBack: minZ = maxZ = ((m_farOrtho / 2.0f) - 0.1); minX = scale * (w / dimMax) + cenX; maxX = -scale * (w / dimMax) + cenX; minY = -scale * (h / dimMax) + cenY; maxY = scale * (h / dimMax) + cenY; normZ = -1.0f; break; case ViewRight: minX = maxX = ((m_farOrtho / 2.0f) - 0.1); minZ = -scale * (w / dimMax) + cenZ; maxZ = scale * (w / dimMax) + cenZ; minY = -scale * (h / dimMax) + cenY; maxY = scale * (h / dimMax) + cenY; normX = 1.0f; break; case ViewLeft: minX = maxX = -((m_farOrtho / 2.0f) - 0.1); minZ = scale * (w / dimMax) + cenZ; maxZ = -scale * (w / dimMax) + cenZ; minY = -scale * (h / dimMax) + cenY; maxY = scale * (h / dimMax) + cenY; normX = -1.0f; break; case ViewTop: minY = maxY = -((m_farOrtho / 2.0f) - 0.1); minX = -scale * (w / dimMax) + cenX; maxX = scale * (w / dimMax) + cenX; minZ = scale * (h / dimMax) + cenZ; maxZ = -scale * (h / dimMax) + cenZ; normY = 1.0f; break; case ViewBottom: minY = maxY = ((m_farOrtho / 2.0f) - 0.1); minX = -scale * (w / dimMax) + cenX; maxX = scale * (w / dimMax) + cenX; minZ = -scale * (h / dimMax) + cenZ; maxZ = scale * (h / dimMax) + cenZ; normY = -1.0f; break; default: break; } if ( m_viewDirection == ViewLeft || m_viewDirection == ViewRight ) { glNormal3f( normX, normY, normZ ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( minX, minY, minZ ); glNormal3f( normX, normY, normZ ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( maxX, maxY, minZ ); glNormal3f( normX, normY, normZ ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( maxX, maxY, maxZ ); glNormal3f( normX, normY, normZ ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( minX, minY, maxZ ); } else { glNormal3f( normX, normY, normZ ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( minX, minY, minZ ); glNormal3f( normX, normY, normZ ); glTexCoord2f( 1.0f, 0.0f ); glVertex3f( maxX, minY, minZ ); glNormal3f( normX, normY, normZ ); glTexCoord2f( 1.0f, 1.0f ); glVertex3f( maxX, maxY, maxZ ); glNormal3f( normX, normY, normZ ); glTexCoord2f( 0.0f, 1.0f ); glVertex3f( minX, maxY, maxZ ); } glEnd(); glDisable( GL_BLEND ); } } } void ModelViewport::drawOverlay() { setViewportOverlay(); glDisable( GL_LIGHTING ); glColor3f( 1.0f, 1.0f, 1.0f ); glEnable( GL_TEXTURE_2D ); int w = this->width(); int h = this->height(); /* glVertex3f( w - , h, 0 ); glVertex3f( w - SCROLL_SIZE, h, 0 ); glVertex3f( w - SCROLL_SIZE, h - SCROLL_SIZE, 0 ); glVertex3f( w, h - SCROLL_SIZE, 0 ); glVertex3f( w - SCROLL_ALL_X, h - SCROLL_ALL_Y, 0 ); glVertex3f( w - SCROLL_ALL_X + SCROLL_SIZE, h - SCROLL_ALL_Y, 0 ); glVertex3f( w - SCROLL_ALL_X + SCROLL_SIZE, h - SCROLL_ALL_Y + SCROLL_SIZE, 0 ); glVertex3f( w - SCROLL_ALL_X, h - SCROLL_ALL_Y + SCROLL_SIZE, 0 ); */ int sx = 0; int sy = 0; int size = SCROLL_SIZE; for ( int b = 0; b < ScrollButtonMAX; b++ ) { ScrollButtonT * sbt = &s_buttons[b]; sx = sbt->x; sy = sbt->y; glBindTexture( GL_TEXTURE_2D, m_scrollTextures[ sbt->texIndex ] ); glBegin( GL_QUADS ); glTexCoord2f( sbt->s1, sbt->t1 ); glVertex3f( w + sx, h + sy, 0 ); glTexCoord2f( sbt->s2, sbt->t2 ); glVertex3f( w + sx + size, h + sy, 0 ); glTexCoord2f( sbt->s3, sbt->t3 ); glVertex3f( w + sx + size, h + sy + size, 0 ); glTexCoord2f( sbt->s4, sbt->t4 ); glVertex3f( w + sx, h + sy + size, 0 ); glEnd(); } glDisable( GL_TEXTURE_2D ); } void ModelViewport::makeTextureFromImage( const QImage & i, GLuint & t ) { glBindTexture( GL_TEXTURE_2D, t ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); int w = i.width(); int h = i.height(); unsigned pixelCount = w * i.height(); uint8_t * data = new uint8_t[ pixelCount * 3 ]; for ( int y = 0; y < h; y ++ ) { for ( int x = 0; x < w; x++ ) { QRgb p = i.pixel( x, h - y - 1 ); data[ ((y * w + x)*3) + 0 ] = qRed( p ); data[ ((y * w + x)*3) + 1 ] = qGreen( p ); data[ ((y * w + x)*3) + 2 ] = qBlue( p ); } } gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, w, h, GL_RGB, GL_UNSIGNED_BYTE, data ); delete[] data; } double ModelViewport::getUnitWidth() { if ( m_viewDirection == ViewPerspective || m_viewDirection == ViewOrtho ) { return g_prefs( "ui_3dgrid_inc" ).doubleValue(); } double unitWidth = g_prefs( "ui_grid_inc" ).doubleValue(); double ratio; int scale_min, scale_max; if ( g_prefs.exists( "ui_grid_mode" ) || !g_prefs.exists( "ui_grid_decimal" ) ) { // Note early return for fixed width ('case 2:') switch ( g_prefs( "ui_grid_mode" ).intValue() ) { default: case 0: // Binary ratio = 2.0; scale_min = 4; scale_max = 16; break; case 1: // Decimal ratio = 10.0; scale_min = 2; scale_max = 60; break; case 2: // Fixed return unitWidth; break; } } else { // ui_grid_decimal is set, and ui_grid_mode is not. if ( g_prefs( "ui_grid_decimal" ).intValue() != 0 ) { ratio = 10.0; scale_min = 2; scale_max = 60; g_prefs( "ui_grid_mode" ) = 1; } else { ratio = 2.0; scale_min = 4; scale_max = 16; g_prefs( "ui_grid_mode" ) = 0; } } double maxDimension = (m_width > m_height) ? m_width : m_height; while ( (maxDimension / unitWidth) > scale_max ) { unitWidth *= ratio; } while ( (maxDimension / unitWidth) < scale_min ) { unitWidth /= ratio; } return unitWidth; } void ModelViewport::updateBackground() { int index = (int) m_viewDirection - 1; std::string filename = m_model->getBackgroundImage( index ); if ( strcmp( filename.c_str(), m_backgroundFile.c_str() ) != 0 ) { m_backgroundFile = filename; if ( m_backgroundFile[0] != '\0' ) { m_texture = TextureManager::getInstance()->getTexture( m_backgroundFile.c_str(), false, false ); if ( !m_texture ) { QString str = tr("Could not load background %1").arg( m_backgroundFile.c_str() ); model_status( m_model, StatusError, STATUSTIME_LONG, "%s", (const char *) str.toUtf8() ); m_texture = TextureManager::getInstance()->getDefaultTexture( m_backgroundFile.c_str() ); } glBindTexture( GL_TEXTURE_2D, m_backgroundTexture ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); GLuint format = m_texture->m_format == Texture::FORMAT_RGBA ? GL_RGBA : GL_RGB; gluBuild2DMipmaps( GL_TEXTURE_2D, format, m_texture->m_width, m_texture->m_height, format, GL_UNSIGNED_BYTE, m_texture->m_data ); } } } void ModelViewport::adjustViewport() { setViewportDraw(); update(); } void ModelViewport::setViewportDraw() { if ( !isValid() ) return; makeCurrent(); m_inOverlay = false; glViewport( 0, 0, ( GLint ) m_viewportWidth, ( GLint ) m_viewportHeight ); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); GLfloat ratio = ( GLfloat ) m_viewportWidth / ( GLfloat ) m_viewportHeight; if ( m_viewDirection == ViewPerspective ) { m_far = m_zoomLevel * 2000.0; m_near = m_zoomLevel * 0.002; gluPerspective( 45.0f, ratio, m_near, m_far ); float x = 0.0; float y = 0.0; if ( m_viewportHeight > m_viewportWidth ) { x = m_zoomLevel; y = x / ratio; } else { y = m_zoomLevel; x = y * ratio; } m_width = x * 2.0; m_height = y * 2.0; } else { float x = 0.0; float y = 0.0; if ( m_viewportHeight > m_viewportWidth ) { x = m_zoomLevel; y = x / ratio; } else { y = m_zoomLevel; x = y * ratio; } #ifdef NEWVIEWPORT glOrtho( m_arcballPoint[0] - x, m_arcballPoint[0] + x, m_arcballPoint[1] - y, m_arcballPoint[1] + y, m_nearOrtho, m_farOrtho ); #else // NEWVIEWPORT glOrtho( m_centerX - x, m_centerX + x, m_centerY - y, m_centerY + y, m_nearOrtho, m_farOrtho ); #endif // NEWVIEWPORT m_width = x * 2.0; m_height = y * 2.0; switch ( m_viewDirection ) { case ViewFront: m_rotX = 0; m_rotY = 0; m_rotZ = 0; break; case ViewBack: m_rotX = 0; m_rotY = 180; m_rotZ = 0; break; case ViewLeft: m_rotX = 0; m_rotY = 90; m_rotZ = 0; break; case ViewRight: m_rotX = 0; m_rotY = -90; m_rotZ = 0; break; case ViewTop: m_rotX = -90; m_rotY = 0; m_rotZ = 0; break; case ViewBottom: m_rotX = 90; m_rotY = 0; m_rotZ = 0; break; default: break; } } m_viewMatrix.loadIdentity(); if ( m_viewDirection == ViewOrtho ) { m_viewMatrix.setRotationInDegrees( m_rotX, m_rotY, m_rotZ ); } else { m_viewMatrix.setRotationInDegrees( -m_rotX, -m_rotY, -m_rotZ ); } #ifdef NEWVIEWPORT m_viewMatrix.setTranslation( -m_arcballPoint[0], -m_arcballPoint[1], -m_arcballPoint[2] ); #else // NEWVIEWPORT m_viewMatrix.setTranslation( -m_centerX, -m_centerY, 0.0 ); #endif // NEWVIEWPORT if ( m_viewDirection == ViewOrtho ) { //m_viewMatrix.setRotationInDegrees( m_arcballPoint[0], m_arcballPoint[1], m_arcballPoint[2] ); } m_invMatrix = m_viewMatrix.getInverse(); glMatrixMode( GL_MODELVIEW ); glLoadIdentity( ); } void ModelViewport::setViewportOverlay() { makeCurrent(); m_inOverlay = true; glViewport( 0, 0, ( GLint ) m_viewportWidth, ( GLint ) m_viewportHeight ); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); glOrtho( 0, this->width(), 0, this->height(), m_nearOrtho, m_farOrtho ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity( ); } void ModelViewport::wheelEvent( QWheelEvent * e ) { if ( e->angleDelta().y() > 0 ) { if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) { rotateClockwise(); } else { zoomIn(); } } else if ( e->angleDelta().y() < 0 ) { if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) { rotateCounterClockwise(); } else { zoomOut(); } } } void ModelViewport::zoomIn() { if ( m_activeButton == Qt::NoButton ) { if ( (m_zoomLevel * VP_ZOOMSCALE) > 0.0001 ) { m_zoomLevel *= (VP_ZOOMSCALE); } m_unitWidth = getUnitWidth(); char str[80]; PORT_snprintf( str, sizeof(str), "Units: %g", m_unitWidth ); model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", str ); QString zoomStr = QString::asprintf( "%f", m_zoomLevel ); emit zoomLevelChanged( zoomStr ); makeCurrent(); adjustViewport(); } } void ModelViewport::zoomOut() { if ( m_activeButton == Qt::NoButton ) { if ( (m_zoomLevel / VP_ZOOMSCALE) < 250000 ) { m_zoomLevel /= VP_ZOOMSCALE; } m_unitWidth = getUnitWidth(); char str[80]; PORT_snprintf( str, sizeof(str), "Units: %g", m_unitWidth ); model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", str ); QString zoomStr = QString::asprintf( "%f", m_zoomLevel ); emit zoomLevelChanged( zoomStr ); makeCurrent(); adjustViewport(); } } void ModelViewport::scrollUp() { m_centerY += m_zoomLevel * 0.10f; m_arcballPoint[1] += m_zoomLevel * 0.10f; makeCurrent(); adjustViewport(); } void ModelViewport::scrollDown() { m_centerY -= m_zoomLevel * 0.10f; m_arcballPoint[1] -= m_zoomLevel * 0.10f; makeCurrent(); adjustViewport(); } void ModelViewport::scrollLeft() { m_centerX -= m_zoomLevel * 0.10f; m_arcballPoint[0] -= m_zoomLevel * 0.10f; makeCurrent(); adjustViewport(); } void ModelViewport::scrollRight() { m_centerX += m_zoomLevel * 0.10f; m_arcballPoint[0] += m_zoomLevel * 0.10f; makeCurrent(); adjustViewport(); } void ModelViewport::rotateUp() { rotateViewport( -15.0 * PIOVER180, 0.0 ); makeCurrent(); adjustViewport(); } void ModelViewport::rotateDown() { rotateViewport( 15.0 * PIOVER180, 0.0 ); makeCurrent(); adjustViewport(); } void ModelViewport::rotateLeft() { rotateViewport( 0.0, -15.0 * PIOVER180 ); makeCurrent(); adjustViewport(); } void ModelViewport::rotateRight() { rotateViewport( 0.0, 15.0 * PIOVER180 ); makeCurrent(); adjustViewport(); } void ModelViewport::rotateClockwise() { rotateViewport( 0.0, 0.0, -15.0 * PIOVER180 ); makeCurrent(); adjustViewport(); } void ModelViewport::rotateCounterClockwise() { rotateViewport( 0.0, 0.0, 15.0 * PIOVER180 ); makeCurrent(); adjustViewport(); } void ModelViewport::mousePressEvent( QMouseEvent * e ) { //printf( "press = %d\n", e->button() ); if ( m_activeButton != Qt::NoButton ) { e->ignore(); return; } e->accept(); m_activeButton = e->button(); m_operation = MO_None; int w = this->width(); int h = this->height(); int bx = e->pos().x(); int by = h - e->pos().y(); m_overlayButton = ScrollButtonMAX; int sx = 0; int sy = 0; int size = SCROLL_SIZE; for ( int b = 0; m_overlayButton == ScrollButtonMAX && b < ScrollButtonMAX; b++ ) { sx = s_buttons[b].x; sy = s_buttons[b].y; if ( (bx >= w + sx) && (bx <= w + sx + size) && (by >= h + sy) && (by <= h + sy + size) ) { m_overlayButton = (ScrollButtonE) b; if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) { m_operation = ( m_overlayButton == ScrollButtonPan ) ? MO_Rotate : MO_RotateButton; } else { m_operation = ( m_overlayButton == ScrollButtonPan ) ? MO_Pan : MO_PanButton; } switch ( m_overlayButton ) { case ScrollButtonPan: m_scrollStartPosition = e->pos(); break; case ScrollButtonUp: if ( m_operation == MO_RotateButton ) rotateUp(); else scrollUp(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; case ScrollButtonDown: if ( m_operation == MO_RotateButton ) rotateDown(); else scrollDown(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; case ScrollButtonLeft: if ( m_operation == MO_RotateButton ) rotateLeft(); else scrollLeft(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; case ScrollButtonRight: if ( m_operation == MO_RotateButton ) rotateRight(); else scrollRight(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; default: break; } } } if ( m_operation == MO_None ) { if ( e->button() == Qt::MidButton ) { m_operation = MO_Pan; m_scrollStartPosition = e->pos(); } else if ( (m_viewDirection == ViewPerspective && e->button() == Qt::LeftButton) || (e->modifiers() & Qt::ControlModifier) ) { m_operation = MO_Rotate; m_scrollStartPosition = e->pos(); } else if ( m_viewDirection != ViewPerspective ) { m_operation = MO_Tool; int button = constructButtonState( e ); ::Tool * tool = m_toolbox->getCurrentTool(); tool->mouseButtonDown( this, button, e->pos().x(), e->pos().y() ); } } /* if ( m_overlayButton == ScrollButtonMAX ) { if ( e->button() == Qt::MidButton || (m_viewDirection == ViewPerspective && e->button() == Qt::LeftButton) ) { m_scrollStartPosition = e->pos(); } else { int button = constructButtonState( e ); ::Tool * tool = m_toolbox->getCurrentTool(); tool->mouseButtonDown( this, button, e->pos().x(), e->pos().y() ); } } */ } void ModelViewport::mouseReleaseEvent( QMouseEvent * e ) { //printf( "release = %d\n", e->button() ); if ( e->button() == m_activeButton ) { if ( m_overlayButton == ScrollButtonMAX ) { e->accept(); if ( m_operation == MO_Tool ) { int button = constructButtonState( e ); ::Tool * tool = m_toolbox->getCurrentTool(); tool->mouseButtonUp( this, button, e->pos().x(), e->pos().y() ); m_model->operationComplete( tool->getName( 0 ) ); if ( m_model->getSelectedProjectionCount() > 0 ) { m_model->setDrawProjections( true ); updateView(); } if ( m_model->getSelectedBoneJointCount() > 0 ) { Model::DrawJointModeE mode = static_cast( g_prefs( "ui_draw_joints" ).intValue() ); if ( mode == Model::JOINTMODE_NONE ) mode = Model::JOINTMODE_BONES; m_model->setDrawJoints( mode ); updateView(); } } } else { m_overlayButton = ScrollButtonMAX; m_scrollTimer->stop(); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Use the middle mouse button to drag/pan the viewport").toUtf8().data() ); } m_activeButton = Qt::NoButton; m_operation = MO_None; } } void ModelViewport::mouseMoveEvent( QMouseEvent * e ) { e->accept(); if ( ! this->hasFocus() ) { this->setFocus(); } if ( m_viewDirection != ViewPerspective ) { int x = e->pos().x(); int y = e->pos().y(); /* double xcoord = 0.0; double ycoord = 0.0; double zcoord = 0.0; getXValue( x, y, &xcoord ); getYValue( x, y, &ycoord ); getZValue( x, y, &zcoord ); */ Vector pos; getParentXYValue( x, y, pos[0], pos[1], true ); m_invMatrix.apply( pos ); for ( int i = 0; i < 3; i++ ) { if ( fabs(pos[i]) < 0.000001 ) pos[i] = 0.0; } char str[80]; PORT_snprintf( str, sizeof(str), "Units: %g (%g, %g, %g)", m_unitWidth, pos[0], pos[1], pos[2] ); model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", str ); } else { model_status( m_model, StatusNormal, STATUSTIME_NONE, "Units: %g", getUnitWidth()); } if ( m_operation == MO_Rotate ) { QPoint curPos = e->pos(); double xDiff = -(m_scrollStartPosition.x() - curPos.x()); double yDiff = -(m_scrollStartPosition.y() - curPos.y()); double rotY = (double) xDiff / (double) this->width() * 3.14159 * 2.0; double rotX = (double) yDiff / (double) this->height() * 3.14159 * 2.0; rotateViewport( rotX, rotY ); m_scrollStartPosition = curPos; } else if ( m_operation == MO_Pan ) { #ifdef NEWVIEWPORT //printf( "adjusting translation\n" ); QPoint curPos = e->pos(); double xDiff = m_scrollStartPosition.x() - curPos.x(); xDiff = xDiff * (m_width / this->width()); m_centerX += xDiff; double yDiff = m_scrollStartPosition.y() - curPos.y(); yDiff = -yDiff; // Adjust for difference between pixel and GL coordinates yDiff = yDiff * (m_height / this->height()); m_centerY += yDiff; //m_viewMatrix.inverseRotateVector( vec ); m_arcballPoint[0] += xDiff; m_arcballPoint[1] += yDiff; /* double vec[3]; vec[0] = xDiff; vec[1] = yDiff; vec[2] = 0.0; vec[0] = m_arcballPoint[0]; vec[1] = m_arcballPoint[1]; vec[2] = m_arcballPoint[2]; m_viewMatrix.inverseRotateVector( vec ); log_debug( "World coords = %f,%f,%f\n", vec[0], vec[1], vec[2] ); */ m_scrollStartPosition = curPos; #else //printf( "adjusting translation\n" ); QPoint curPos = e->pos(); double xDiff = m_scrollStartPosition.x() - curPos.x(); m_centerX += xDiff * (m_width / this->width()); double yDiff = m_scrollStartPosition.y() - curPos.y(); yDiff = -yDiff; // Adjust for difference between pixel and GL coordinates m_centerY += yDiff * (m_height / this->height()); m_scrollStartPosition = curPos; #endif // NEWVIEWPORT makeCurrent(); adjustViewport(); } else if ( m_operation == MO_Tool ) { //printf( "tool mouse event\n" ); int button = constructButtonState( e ); ::Tool * tool = m_toolbox->getCurrentTool(); tool->mouseButtonMove( this, button, e->pos().x(), e->pos().y() ); } #if 0 if ( m_overlayButton == ScrollButtonMAX ) { if ( m_activeButton ) { if ( m_activeButton == Qt::MidButton || (m_viewDirection == ViewPerspective && m_activeButton == Qt::LeftButton) ) { if ( m_viewDirection == ViewPerspective && (m_activeButton == Qt::LeftButton) ) { QPoint curPos = e->pos(); double xDiff = -(m_scrollStartPosition.x() - curPos.x()); double yDiff = -(m_scrollStartPosition.y() - curPos.y()); Matrix mcur; Matrix mcurinv; double rot[3]; rot[0] = m_rotX * PIOVER180; rot[1] = m_rotY * PIOVER180; rot[2] = m_rotZ * PIOVER180; mcur.setRotation( rot ); mcurinv = mcur.getInverse(); #ifdef NEWVIEWPORT mcur.inverseRotateVector( m_arcballPoint ); #endif // NEWVIEWPORT Vector yvec; yvec.set( 0, 0.0 ); yvec.set( 1, 1.0 ); yvec.set( 2, 0.0 ); yvec.set( 3, 0.0 ); Vector xvec; xvec.set( 0, 1.0 ); xvec.set( 1, 0.0 ); xvec.set( 2, 0.0 ); xvec.set( 3, 0.0 ); Matrix mx; Matrix my; double rotY = (double) xDiff / (double) this->width() * 3.14159 * 2.0; double rotX = (double) yDiff / (double) this->height() * 3.14159 * 2.0; yvec = yvec * mcurinv; my.setRotationOnAxis( yvec.getVector(), rotY ); xvec = xvec * mcurinv; mx.setRotationOnAxis( xvec.getVector(), rotX ); mcur = mx * mcur; mcur = my * mcur; mcur.getRotation( rot ); m_rotX = rot[0] / PIOVER180; m_rotY = rot[1] / PIOVER180; m_rotZ = rot[2] / PIOVER180; //m_rotY += xDiff * PIOVER180 * 14.0; //m_rotX += yDiff * PIOVER180 * 14.0; #ifdef NEWVIEWPORT mcur.apply3( m_arcballPoint ); #endif // NEWVIEWPORT m_scrollStartPosition = curPos; // And finally, update the view makeCurrent(); adjustViewport(); } else { #ifdef NEWVIEWPORT //printf( "adjusting translation\n" ); QPoint curPos = e->pos(); double xDiff = m_scrollStartPosition.x() - curPos.x(); xDiff = xDiff * (m_width / this->width()); m_centerX += xDiff; double yDiff = m_scrollStartPosition.y() - curPos.y(); yDiff = -yDiff; // Adjust for difference between pixel and GL coordinates yDiff = yDiff * (m_height / this->height()); m_centerY += yDiff; //m_viewMatrix.inverseRotateVector( vec ); m_arcballPoint[0] += xDiff; m_arcballPoint[1] += yDiff; /* double vec[3]; vec[0] = xDiff; vec[1] = yDiff; vec[2] = 0.0; vec[0] = m_arcballPoint[0]; vec[1] = m_arcballPoint[1]; vec[2] = m_arcballPoint[2]; m_viewMatrix.inverseRotateVector( vec ); log_debug( "World coords = %f,%f,%f\n", vec[0], vec[1], vec[2] ); */ m_scrollStartPosition = curPos; #else //printf( "adjusting translation\n" ); QPoint curPos = e->pos(); double xDiff = m_scrollStartPosition.x() - curPos.x(); m_centerX += xDiff * (m_width / this->width()); double yDiff = m_scrollStartPosition.y() - curPos.y(); yDiff = -yDiff; // Adjust for difference between pixel and GL coordinates m_centerY += yDiff * (m_height / this->height()); m_scrollStartPosition = curPos; #endif // NEWVIEWPORT makeCurrent(); adjustViewport(); } } else { //printf( "tool mouse event\n" ); int button = constructButtonState( e ); ::Tool * tool = m_toolbox->getCurrentTool(); tool->mouseButtonMove( this, button, e->pos().x(), e->pos().y() ); } } } else { if ( m_overlayButton == ScrollButtonPan ) { //printf( "adjusting translation\n" ); QPoint curPos = e->pos(); double xDiff = m_scrollStartPosition.x() - curPos.x(); xDiff = xDiff * (m_width / this->width()); m_centerX += xDiff; double yDiff = m_scrollStartPosition.y() - curPos.y(); yDiff = -yDiff; // Adjust for difference between pixel and GL coordinates yDiff = yDiff * (m_height / this->height()); m_centerY += yDiff; m_arcballPoint[0] += xDiff; m_arcballPoint[1] += yDiff; m_scrollStartPosition = curPos; makeCurrent(); adjustViewport(); } } #endif // 0 } void ModelViewport::keyPressEvent( QKeyEvent * e ) { if ( m_activeButton == Qt::NoButton ) { switch ( e->key() ) { case Qt::Key_Equal: case Qt::Key_Plus: { if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) { rotateClockwise(); } else { zoomIn(); } } break; case Qt::Key_Minus: case Qt::Key_Underscore: { if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) { rotateCounterClockwise(); } else { zoomOut(); } } break; case Qt::Key_QuoteLeft: { int newDir = 0; switch ( m_viewDirection ) { case ViewPerspective: newDir = ViewOrtho; break; case ViewFront: case ViewBack: case ViewRight: case ViewLeft: case ViewTop: case ViewBottom: case ViewOrtho: newDir = ViewPerspective; break; default: break; } emit viewDirectionChanged( newDir ); } break; case Qt::Key_Backslash: { int newDir = 0; switch ( m_viewDirection ) { case ViewFront: newDir = ViewBack; break; case ViewBack: newDir = ViewFront; break; case ViewRight: newDir = ViewLeft; break; case ViewLeft: newDir = ViewRight; break; case ViewTop: newDir = ViewBottom; break; case ViewBottom: newDir = ViewTop; break; case ViewPerspective: newDir = ViewOrtho; break; case ViewOrtho: newDir = ViewPerspective; break; default: break; } emit viewDirectionChanged( newDir ); } break; case Qt::Key_0: m_centerX = 0.0; m_centerY = 0.0; m_arcballPoint[0] = 0.0; m_arcballPoint[1] = 0.0; m_arcballPoint[2] = 0.0; makeCurrent(); adjustViewport(); break; case Qt::Key_Up: if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) rotateUp(); else scrollUp(); break; case Qt::Key_Down: if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) rotateDown(); else scrollDown(); break; case Qt::Key_Left: if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) rotateLeft(); else scrollLeft(); break; case Qt::Key_Right: if ( (e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) rotateRight(); else scrollRight(); break; case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: if ( e->modifiers() & Qt::ControlModifier ) { log_debug( "set viewport %d\n", e->key() - Qt::Key_1 ); int slot = ( (int) e->key() - (int) Qt::Key_1 ); ViewStateT viewState; viewState.direction = m_viewDirection; viewState.zoom = m_zoomLevel; viewState.rotation[0] = m_rotX; viewState.rotation[1] = m_rotY; viewState.rotation[2] = m_rotZ; viewState.translation[0] = m_arcballPoint[0]; viewState.translation[1] = m_arcballPoint[1]; viewState.translation[2] = m_arcballPoint[2]; emit viewportSaveState( slot, viewState ); } else { log_debug( "viewport recall %d\n", e->key() - Qt::Key_1 ); int slot = ( (int) e->key() - (int) Qt::Key_1 ); emit viewportRecallState( slot ); return; } break; default: QOpenGLWidget::keyPressEvent( e ); return; break; } } e->accept(); } void ModelViewport::rotateViewport( double rotX, double rotY, double rotZ ) { if ( fabs( rotX ) >0.00001 || fabs( rotY ) > 0.00001 || fabs( rotZ ) > 0.00001 ) { if ( m_viewDirection != ViewPerspective && m_viewDirection != ViewOrtho ) { emit viewDirectionChanged( ViewOrtho ); } Matrix mcur; Matrix mcurinv; double rot[3]; rot[0] = m_rotX * PIOVER180; rot[1] = m_rotY * PIOVER180; rot[2] = m_rotZ * PIOVER180; mcur.setRotation( rot ); mcurinv = mcur.getInverse(); #ifdef NEWVIEWPORT mcur.inverseRotateVector( m_arcballPoint ); #endif // NEWVIEWPORT Vector yvec; yvec.set( 0, 0.0 ); yvec.set( 1, 1.0 ); yvec.set( 2, 0.0 ); yvec.set( 3, 0.0 ); Vector xvec; xvec.set( 0, 1.0 ); xvec.set( 1, 0.0 ); xvec.set( 2, 0.0 ); xvec.set( 3, 0.0 ); Vector zvec; zvec.set( 0, 0.0 ); zvec.set( 1, 0.0 ); zvec.set( 2, 1.0 ); zvec.set( 3, 0.0 ); Matrix mx; Matrix my; Matrix mz; zvec = zvec * mcurinv; mz.setRotationOnAxis( zvec.getVector(), rotZ ); yvec = yvec * mcurinv; my.setRotationOnAxis( yvec.getVector(), rotY ); xvec = xvec * mcurinv; mx.setRotationOnAxis( xvec.getVector(), rotX ); mcur = mx * mcur; mcur = my * mcur; mcur = mz * mcur; mcur.getRotation( rot ); m_rotX = rot[0] / PIOVER180; m_rotY = rot[1] / PIOVER180; m_rotZ = rot[2] / PIOVER180; //m_rotY += xDiff * PIOVER180 * 14.0; //m_rotX += yDiff * PIOVER180 * 14.0; #ifdef NEWVIEWPORT mcur.apply3( m_arcballPoint ); #endif // NEWVIEWPORT // And finally, update the view makeCurrent(); adjustViewport(); } } void ModelViewport::setViewState( const ViewStateT & viewState ) { m_viewDirection = viewState.direction; m_zoomLevel = viewState.zoom; m_rotX = viewState.rotation[0]; m_rotY = viewState.rotation[1]; m_rotZ = viewState.rotation[2]; m_arcballPoint[0] = viewState.translation[0]; m_arcballPoint[1] = viewState.translation[1]; m_arcballPoint[2] = viewState.translation[2]; makeCurrent(); adjustViewport(); } void ModelViewport::viewChangeEvent( int dir ) { log_debug( "viewChangeEvent( %d )\n", dir ); if ( dir == m_viewDirection ) { return; } if ( m_viewDirection == ViewPerspective ) //|| m_viewDirection == ViewOrtho ) { Matrix m; m.setRotationInDegrees( 0.0f, 0.0f, -m_rotZ ); m.apply3( m_arcballPoint ); m.setRotationInDegrees( 0.0f, -m_rotY, 0.0f ); m.apply3( m_arcballPoint ); m.setRotationInDegrees( -m_rotX, 0.0f, 0.0f ); m.apply3( m_arcballPoint ); } else { m_invMatrix.apply3( m_arcballPoint ); } log_debug( "center point = %f,%f,%f\n", m_arcballPoint[0], m_arcballPoint[1], m_arcballPoint[2] ); bool toFree = false; switch ( m_viewDirection ) { case ViewFront: case ViewBack: case ViewRight: case ViewLeft: case ViewTop: case ViewBottom: if ( dir == ViewPerspective || dir == ViewOrtho ) { toFree = true; } break; default: break; } if ( toFree ) { log_debug( "inverting rotation\n" ); m_rotX = -m_rotX; m_rotY = -m_rotY; m_rotZ = -m_rotZ; } else { switch ( dir ) { case ViewFront: m_rotX = 0; m_rotY = 0; m_rotZ = 0; break; case ViewBack: m_rotX = 0; m_rotY = 180; m_rotZ = 0; break; case ViewLeft: m_rotX = 0; m_rotY = 90; m_rotZ = 0; break; case ViewRight: m_rotX = 0; m_rotY = -90; m_rotZ = 0; break; case ViewTop: m_rotX = -90; m_rotY = 0; m_rotZ = 0; break; case ViewBottom: m_rotX = 90; m_rotY = 0; m_rotZ = 0; break; default: break; } } Matrix m; m.loadIdentity(); if ( false || (m_viewDirection == ViewPerspective && dir == ViewOrtho) || (m_viewDirection == ViewOrtho && dir == ViewPerspective) ) { m.setRotationInDegrees( m_rotX, m_rotY, m_rotZ ); } else { m.setRotationInDegrees( -m_rotX, -m_rotY, -m_rotZ ); } if ( toFree ) { m = m.getInverse(); } m.apply3( m_arcballPoint ); log_debug( "after point = %f,%f,%f\n", m_arcballPoint[0], m_arcballPoint[1], m_arcballPoint[2] ); m_viewDirection = static_cast( dir ); makeCurrent(); adjustViewport(); } void ModelViewport::setZoomLevel( double zoom ) { if ( m_activeButton == Qt::NoButton ) { m_zoomLevel = zoom; makeCurrent(); adjustViewport(); QString zoomStr = QString::asprintf( "%f", m_zoomLevel ); emit zoomLevelChanged( zoomStr ); } } void ModelViewport::scrollTimeout() { if ( m_operation == MO_RotateButton ) { switch ( m_overlayButton ) { case ScrollButtonUp: rotateUp(); break; case ScrollButtonDown: rotateDown(); break; case ScrollButtonLeft: rotateLeft(); break; case ScrollButtonRight: rotateRight(); break; default: m_scrollTimer->stop(); return; } } else { switch ( m_overlayButton ) { case ScrollButtonUp: scrollUp(); break; case ScrollButtonDown: scrollDown(); break; case ScrollButtonLeft: scrollLeft(); break; case ScrollButtonRight: scrollRight(); break; default: m_scrollTimer->stop(); return; } } m_scrollTimer->setSingleShot( false ); m_scrollTimer->start( 100 ); } void ModelViewport::focusInEvent( QFocusEvent * e ) { if ( !isValid() ) return; if ( useDarkBackground() ) { m_backColor.setRgb( 150 / 2, 210 / 2, 210 / 2 ); } else { m_backColor.setRgb( 150, 210, 210 ); } makeCurrent(); glClearColor( m_backColor.red() / 256.0, m_backColor.green() / 256.0, m_backColor.blue() / 256.0, 1.0f ); update(); } void ModelViewport::focusOutEvent( QFocusEvent * e ) { if ( !isValid() ) return; if ( useDarkBackground() ) { m_backColor.setRgb( 130 / 2, 200 / 2, 200 / 2 ); } else { m_backColor.setRgb( 130, 200, 200 ); } makeCurrent(); glClearColor( m_backColor.red() / 256.0, m_backColor.green() / 256.0, m_backColor.blue() / 256.0, 1.0f ); update(); } /* void ModelViewport::dragEnterEvent( QDragMoveEvent * e ) { log_debug( "got drag enter event\n" ); if ( QUriDrag::canDecode( e ) ) { log_debug( "is URI\n" ); } if ( QTextDrag::canDecode( e ) ) { log_debug( "is Text\n" ); } if ( QImageDrag::canDecode( e ) ) { log_debug( "is Image\n" ); } //e->accept( QRect( 0, 0, this->width(), this->height() ) ); } */ void ModelViewport::getParentXYValue( int x, int y, double & xval, double & yval, bool selected ) { xval = (((double) x / (double) this->width()) * m_width) - (m_width / 2.0); yval = (((double) -y / (double) this->height()) * m_height) + (m_height / 2.0); double maxDist = (4.1 / (double) this->width()) * m_width; if ( g_prefs.exists( "ui_snap_vertex" ) && g_prefs( "ui_snap_vertex" ).intValue() != 0 ) { // snap to vertex double curDist = maxDist; int curIndex = -1; Model::PositionTypeE curType = Model::PT_Vertex; const Matrix & mat = getParentViewMatrix(); double coord[3] = { 0, 0, 0 }; double saveCoord[3] = { 0, 0, 0 }; size_t vcount = m_model->getVertexCount(); for ( size_t v = 0; v < vcount; v++ ) { if ( selected || !m_model->isVertexSelected( v ) ) { m_model->getVertexCoords( v, coord ); mat.apply3( coord ); coord[0] += mat.get(3,0); coord[1] += mat.get(3,1); coord[2] += mat.get(3,2); double xdiff = coord[0] - xval; double ydiff = coord[1] - yval; double diff = sqrt( xdiff*xdiff + ydiff*ydiff ); if ( diff < curDist ) { curDist = diff; curIndex = v; curType = Model::PT_Vertex; saveCoord[0] = coord[0]; saveCoord[1] = coord[1]; saveCoord[2] = coord[2]; } } } size_t bcount = m_model->getBoneJointCount(); for ( size_t b = 0; b < bcount; b++ ) { if ( selected || !m_model->isBoneJointSelected( b ) ) { m_model->getBoneJointCoords( b, coord ); mat.apply3( coord ); coord[0] += mat.get(3,0); coord[1] += mat.get(3,1); coord[2] += mat.get(3,2); double xdiff = coord[0] - xval; double ydiff = coord[1] - yval; double diff = sqrt( xdiff*xdiff + ydiff*ydiff ); if ( diff < curDist ) { curDist = diff; curIndex = b; curType = Model::PT_Joint; saveCoord[0] = coord[0]; saveCoord[1] = coord[1]; saveCoord[2] = coord[2]; } } } size_t p; size_t pcount = m_model->getPointCount(); for ( p = 0; p < pcount; p++ ) { if ( selected || !m_model->isPointSelected( p ) ) { m_model->getPointCoords( p, coord ); mat.apply3( coord ); coord[0] += mat.get(3,0); coord[1] += mat.get(3,1); coord[2] += mat.get(3,2); double xdiff = coord[0] - xval; double ydiff = coord[1] - yval; double diff = sqrt( xdiff*xdiff + ydiff*ydiff ); if ( diff < curDist ) { curDist = diff; curIndex = p; curType = Model::PT_Point; saveCoord[0] = coord[0]; saveCoord[1] = coord[1]; saveCoord[2] = coord[2]; } } } pcount = m_model->getProjectionCount(); for ( p = 0; p < pcount; p++ ) { if ( selected || !m_model->isProjectionSelected( p ) ) { m_model->getProjectionCoords( p, coord ); mat.apply3( coord ); coord[0] += mat.get(3,0); coord[1] += mat.get(3,1); coord[2] += mat.get(3,2); double xdiff = coord[0] - xval; double ydiff = coord[1] - yval; double diff = sqrt( xdiff*xdiff + ydiff*ydiff ); if ( diff < curDist ) { curDist = diff; curIndex = p; curType = Model::PT_Projection; saveCoord[0] = coord[0]; saveCoord[1] = coord[1]; saveCoord[2] = coord[2]; } } } if ( curIndex >= 0 ) { xval = saveCoord[0]; yval = saveCoord[1]; maxDist = 0.0; } } if ( m_viewDirection != ViewOrtho && g_prefs.exists( "ui_snap_grid" ) && g_prefs( "ui_snap_grid" ).intValue() != 0 ) { // snap to grid double val; int mult; double fudge; fudge = 0.5; #ifdef NEWVIEWPORT double x = xval + m_arcballPoint[0]; #else // NEWVIEWPORT double x = xval + m_centerX; #endif // NEWVIEWPORT if ( x < 0.0 ) { fudge = -0.5; } mult = (int) (x / m_unitWidth + fudge); val = (double) mult * m_unitWidth; if ( fabs( x - val ) < maxDist ) { #ifdef NEWVIEWPORT xval = val - m_arcballPoint[0]; #else // NEWVIEWPORT xval = val - m_centerX; #endif // NEWVIEWPORT } fudge = 0.5; #ifdef NEWVIEWPORT double y = yval + m_arcballPoint[1]; #else // NEWVIEWPORT double y = yval + m_centerY; #endif // NEWVIEWPORT if ( y < 0.0 ) { fudge = -0.5; } mult = (int) (y / m_unitWidth + fudge); val = (double) mult * m_unitWidth; if ( fabs( y - val ) < maxDist ) { #ifdef NEWVIEWPORT yval = val - m_arcballPoint[1]; #else // NEWVIEWPORT yval = val - m_centerY; #endif // NEWVIEWPORT } } } void ModelViewport::getRawParentXYValue( int x, int y, double & xval, double & yval ) { xval = (((double) x / (double) this->width()) * m_width) - (m_width / 2.0); yval = (((double) -y / (double) this->height()) * m_height) + (m_height / 2.0); } #ifdef NEWVIEWPORT bool ModelViewport::getXValue( int x, int y, double * val ) { Vector vec; vec[0] = (((double) x / (double) this->width()) * m_width) - (m_width / 2.0); vec[1] = -((((double) y / (double) this->height()) * m_height) - (m_height / 2.0)); vec[2] = 0.0; m_invMatrix.apply( vec ); *val = vec[0]; switch ( m_viewDirection ) { case ViewFront: case ViewTop: case ViewBottom: case ViewBack: case ViewOrtho: return true; default: break; } return false; } bool ModelViewport::getYValue( int x, int y, double * val ) { Vector vec; vec[0] = (((double) x / (double) this->width()) * m_width) - (m_width / 2.0); vec[1] = -((((double) y / (double) this->height()) * m_height) - (m_height / 2.0)); vec[2] = 0.0; m_invMatrix.apply( vec ); *val = vec[1]; switch ( m_viewDirection ) { case ViewFront: case ViewBack: case ViewLeft: case ViewRight: case ViewOrtho: return true; default: break; } return false; } bool ModelViewport::getZValue( int x, int y, double * val ) { Vector vec; vec[0] = (((double) x / (double) this->width()) * m_width) - (m_width / 2.0); vec[1] = -((((double) y / (double) this->height()) * m_height) - (m_height / 2.0)); vec[2] = 0.0; m_invMatrix.apply( vec ); *val = vec[2]; switch ( m_viewDirection ) { case ViewLeft: case ViewRight: case ViewTop: case ViewBottom: case ViewOrtho: return true; default: break; } return false; } #else // NEWVIEWPORT bool ModelViewport::getXValue( int x, int y, double * val ) { switch ( m_viewDirection ) { case ViewFront: case ViewTop: case ViewBottom: *val = (m_centerX - (m_width / 2.0)) + (((double) x / (double) this->width()) * m_width); break; case ViewBack: *val = ((-m_centerX) + (m_width / 2.0)) + (((double) (-x) / (double) this->width()) * m_width); break; case ViewRight: case ViewLeft: default: return false; } return true; } bool ModelViewport::getYValue( int x, int y, double * val ) { switch ( m_viewDirection ) { case ViewFront: case ViewBack: case ViewRight: case ViewLeft: *val = (m_centerY + (m_height / 2.0)) - (((double) y / (double) this->height()) * m_height); break; case ViewTop: case ViewBottom: default: return false; break; } return true; } bool ModelViewport::getZValue( int x, int y, double * val ) { switch ( m_viewDirection ) { case ViewTop: *val = ((-m_centerY) - (m_height / 2.0)) - (((double) (-y) / (double) this->height()) * m_height); break; case ViewBottom: *val = (m_centerY + (m_height / 2.0)) - (((double) y / (double) this->height()) * m_height); break; case ViewRight: *val = (m_centerX - (m_width / 2.0)) + (((double) x / (double) this->width()) * m_width); break; case ViewLeft: *val = ((-m_centerX) + (m_width / 2.0)) + (((double) (-x) / (double) this->width()) * m_width); break; case ViewFront: case ViewBack: default: return false; } return true; } #endif // NEWVIEWPORT int ModelViewport::constructButtonState( QMouseEvent * e ) { int button = 0; //switch ( e->button() ) switch ( m_activeButton ) { case Qt::LeftButton: button = ::Tool::BS_Left; break; case Qt::MidButton: button = ::Tool::BS_Middle; break; case Qt::RightButton: button = ::Tool::BS_Right; break; default: break; } if ( e->modifiers() & Qt::ShiftModifier ) { button |= ::Tool::BS_Shift; } if ( e->modifiers() & Qt::AltModifier ) { button |= ::Tool::BS_Alt; } if ( e->modifiers() & Qt::ControlModifier ) { button |= ::Tool::BS_Ctrl; } return button; } void ModelViewport::updateView() { update(); StatusObject * bar = model_status_get_object( m_model ); bar->setVertices( m_model->getVertexCount(), m_model->getSelectedVertexCount() ); bar->setFaces( m_model->getTriangleCount(), m_model->getSelectedTriangleCount() ); bar->setGroups( m_model->getGroupCount() ); bar->setBoneJoints( m_model->getBoneJointCount(), m_model->getSelectedBoneJointCount() ); bar->setPoints( m_model->getPointCount(), m_model->getSelectedPointCount() ); bar->setTextures( m_model->getTextureCount() ); } void ModelViewport::update3dView() { if ( m_viewDirection == ViewPerspective ) { updateView(); } } void ModelViewport::addDecal( Decal * decal ) { m_decals.push_back( decal ); update(); } void ModelViewport::removeDecal( Decal * decal ) { m_decals.remove( decal ); update(); } void ModelViewport::frameArea( double x1, double y1, double z1, double x2, double y2, double z2 ) { double centerX = (x1 + x2) / 2.0; double centerY = (y1 + y2) / 2.0; double centerZ = (z1 + z2) / 2.0; double width = fabs( x1 - x2 ); double height = fabs( y1 - y2 ); double depth = fabs( z1 - z2 ); if ( width < 0.0001667 ) { width = 0.0001667; } if ( height < 0.0001667 ) { height = 0.0001667; } if ( depth < 0.0001667 ) { depth = 0.0001667; } width *= 1.20; height *= 1.20; depth *= 1.20; Vector bounds; bounds[0] = width; bounds[1] = height; bounds[2] = depth; double viewWidth = 0.0; double viewHeight = 0.0; switch ( m_viewDirection ) { #ifdef NEWVIEWPORT case ViewFront: case ViewBack: case ViewRight: case ViewLeft: case ViewTop: case ViewBottom: case ViewOrtho: case ViewPerspective: { m_arcballPoint[0] = centerX; m_arcballPoint[1] = centerY; m_arcballPoint[2] = centerZ; //log_debug( "view = %d (%f,%f,%f)\n", // (int) m_viewDirection, // m_rotX, m_rotY, m_rotZ ); double rx = m_rotX; double ry = m_rotY; double rz = m_rotZ; if ( m_viewDirection != ViewPerspective || m_viewDirection == ViewOrtho ) { rx = -rx; ry = -ry; rz = -rz; } //m_viewMatrix.apply3( m_arcballPoint ); if ( m_viewDirection == ViewOrtho ) { Matrix m; m_viewMatrix.apply3( m_arcballPoint ); m_viewMatrix.apply3( bounds ); /* m.setRotationInDegrees( rx, 0.0f, 0.0f ); m.apply3( m_arcballPoint ); m.apply3( bounds ); m.setRotationInDegrees( 0.0f, ry, 0.0f ); m.apply3( m_arcballPoint ); m.apply3( bounds ); m.setRotationInDegrees( 0.0f, 0.0f, rz ); m.apply3( m_arcballPoint ); m.apply3( bounds ); */ } else { Matrix m; m.setRotationInDegrees( rx, 0.0f, 0.0f ); m.apply3( m_arcballPoint ); m.apply3( bounds ); m.setRotationInDegrees( 0.0f, ry, 0.0f ); m.apply3( m_arcballPoint ); m.apply3( bounds ); m.setRotationInDegrees( 0.0f, 0.0f, rz ); m.apply3( m_arcballPoint ); m.apply3( bounds ); } if ( m_viewDirection == ViewPerspective || m_viewDirection == ViewOrtho ) { viewWidth = viewHeight = sqrt( height*height + width*width + depth*depth ) * 1.2; } else { viewWidth = fabs( bounds[0] ); viewHeight = fabs( bounds[1] ); } //log_debug( "point = %d (%f,%f,%f)\n", // (int) m_viewDirection, // m_arcballPoint[0], m_arcballPoint[1], m_arcballPoint[2] ); } break; default: return; break; #else // NEWVIEWPORT case ViewFront: m_centerX = centerX; m_centerY = centerY; viewWidth = width; viewHeight = height; break; case ViewBack: m_centerX = -centerX; m_centerY = centerY; viewWidth = width; viewHeight = height; break; case ViewRight: m_centerX = centerZ; m_centerY = centerY; viewWidth = depth; viewHeight = height; break; case ViewLeft: m_centerX = -centerZ; m_centerY = centerY; viewWidth = depth; viewHeight = height; break; case ViewTop: m_centerX = centerX; m_centerY = -centerZ; viewWidth = width; viewHeight = depth; break; case ViewBottom: m_centerX = centerX; m_centerY = centerZ; viewWidth = width; viewHeight = depth; break; case ViewPerspective: { double rotation[3]; rotation[0] = m_rotX * PIOVER180; rotation[1] = m_rotY * PIOVER180; rotation[2] = m_rotZ * PIOVER180; Matrix m; m.setRotation( rotation ); double v[3]; v[0] = centerX; v[1] = centerY; v[2] = centerZ; m.inverseRotateVector( v ); m_centerX = v[0]; m_centerY = v[1]; viewWidth = height; viewHeight = width; } break; default: return; break; #endif // NEWVIEWPORT } if ( viewWidth > viewHeight ) { if ( m_viewDirection == ViewPerspective ) { m_zoomLevel = viewWidth / 2.0; } else { m_zoomLevel = viewWidth / 2.0; } } else { if ( m_viewDirection == ViewPerspective ) { m_zoomLevel = viewHeight / 2.0; } else { m_zoomLevel = viewHeight / 2.0; } } QString zoomStr = QString::asprintf( "%f", m_zoomLevel ); emit zoomLevelChanged( zoomStr ); makeCurrent(); adjustViewport(); } void ModelViewport::checkGlErrors() { int error = glGetError(); if ( error ) { switch ( error ) { case GL_INVALID_VALUE: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Invalid Value").toUtf8().data() ); break; case GL_INVALID_ENUM: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Invalid Enum").toUtf8().data() ); break; case GL_INVALID_OPERATION: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Invalid Operation").toUtf8().data() ); break; case GL_STACK_OVERFLOW: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Stack Overflow").toUtf8().data() ); break; case GL_STACK_UNDERFLOW: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Stack Underflow").toUtf8().data() ); break; case GL_OUT_OF_MEMORY: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Out Of Memory").toUtf8().data() ); break; default: model_status( m_model, StatusNormal, STATUSTIME_NONE, "%s", tr("OpenGL error = Unknown").toUtf8().data() ); break; } } } void ModelViewport::copyContentsToTexture( Texture * tex ) { if ( !isValid() ) return; makeCurrent(); m_capture = true; if ( tex ) { unsigned w = m_viewportWidth; unsigned h = m_viewportHeight; // make sure texture can hold data if ( tex->m_data ) { unsigned bpp = (tex->m_format == Texture::FORMAT_RGB) ? 3 : 4; if ( (tex->m_width * tex->m_height * bpp) < (w * h * 4) ) { delete[] tex->m_data; tex->m_data = NULL; } } if ( tex->m_data == NULL ) { tex->m_data = new uint8_t[ w * h * 4 ]; } tex->m_width = w; tex->m_height = h; tex->m_format = Texture::FORMAT_RGBA; makeCurrent(); update(); glReadPixels( 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, tex->m_data ); } makeCurrent(); glClearColor( m_backColor.red() / 256.0, m_backColor.green() / 256.0, m_backColor.blue() / 256.0, 1.0f ); m_capture = false; } void ModelViewport::updateCaptureGL() { m_capture = true; makeCurrent(); update(); m_capture = false; } mm3d-1.3.15/src/depui/modelviewport.h000066400000000000000000000153141466047437300174310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MVIEWPORT_H #define __MVIEWPORT_H #include #include #include #include #include #include #include #include "tool.h" #include "decal.h" #include "texture.h" #include #include using std::list; class Model; class Decal; class Toolbox; class QTimer; typedef list DecalList; class ModelViewport : public QOpenGLWidget, public Tool::Parent { Q_OBJECT public: enum { MAX_VERTICAL_UNITS = 32767, MAX_HORIZONTAL_UNITS = 32767 }; enum _ViewOptions_e { ViewWireframe, ViewFlat, ViewSmooth, ViewTexture, ViewAlpha }; typedef enum _ViewOptions_e ViewOptionsE; enum _MouseOperation_e { MO_None, MO_Tool, MO_Pan, MO_PanButton, MO_Rotate, MO_RotateButton, }; typedef enum _MouseOperation_e MouseOperationE; enum _ScrollButton_e { ScrollButtonPan, ScrollButtonLeft, ScrollButtonRight, ScrollButtonUp, ScrollButtonDown, ScrollButtonMAX }; typedef enum _ScrollButton_e ScrollButtonE; struct _ViewState_t { ViewDirectionE direction; double rotation[3]; double translation[3]; double zoom; }; typedef struct _ViewState_t ViewStateT; ModelViewport( QWidget * parent = NULL ); virtual ~ModelViewport(); void freeTextures(); double getZoomLevel() { return m_zoomLevel; }; void frameArea( double x1, double y1, double z1, double x2, double y2, double z2 ); void zoomIn(); void zoomOut(); void scrollUp(); void scrollDown(); void scrollLeft(); void scrollRight(); void rotateUp(); void rotateDown(); void rotateLeft(); void rotateRight(); void rotateClockwise(); void rotateCounterClockwise(); void rotateViewport( double x, double y, double z = 0.0 ); void setModel( Model * model ) { m_model = model; }; void setToolbox( Toolbox * toolbox ) { m_toolbox = toolbox; }; int constructButtonState( QMouseEvent * e ); // Tool::Parent methods Model * getModel() override { return m_model; }; ViewDirectionE getViewDirection() override { return m_viewDirection; }; void updateView() override; void update3dView() override; void updateAllViews() override { emit modelUpdated(); }; void getParentXYValue( int x, int y, double & xval, double & yval, bool selected ) override; void getRawParentXYValue( int x, int y, double & xval, double & yval ) override; const Matrix & getParentViewMatrix() const override { return m_viewMatrix; }; const Matrix & getParentViewInverseMatrix() const override { return m_invMatrix; }; bool getXValue( int x, int y, double * val ) override; bool getYValue( int x, int y, double * val ) override; bool getZValue( int x, int y, double * val ) override; void addDecal( Decal * decal ) override; void removeDecal( Decal * decal ) override; void copyContentsToTexture( Texture * tex ); void updateCaptureGL(); signals: void zoomLevelChanged( const QString & zoomStr ); void viewDirectionChanged( int dir ); void modelUpdated(); void viewportSaveState( int slotNumber, const ModelViewport::ViewStateT & viewState ); void viewportRecallState( int slotNumber ); public slots: void viewChangeEvent( int dir ); void setZoomLevel( double zoomLevel ); void setViewState( const ModelViewport::ViewStateT & viewState ); void scrollTimeout(); protected slots: void wheelEvent( QWheelEvent * e ) override; void mouseMoveEvent( QMouseEvent * e ) override; void mousePressEvent( QMouseEvent * e ) override; void mouseReleaseEvent( QMouseEvent * e ) override; void keyPressEvent( QKeyEvent * e ) override; void focusInEvent( QFocusEvent * e ) override; void focusOutEvent( QFocusEvent * e ) override; //void dragEnterEvent( QDragMoveEvent * e ) override; protected: #if QT_VERSION < QT_VERSION_CHECK( 5, 6, 0 ) qreal devicePixelRatioF() { return devicePixelRatio(); } #endif void initializeGL() override; void paintGL() override; void resizeGL( int w, int h ) override; void checkGlErrors(); void updateBackground(); void adjustViewport(); void setViewportDraw(); void setViewportOverlay(); void drawGridLines(); void drawOrigin(); void drawBackground(); void drawOverlay(); void makeTextureFromImage( const QImage & i, GLuint & t ); double getUnitWidth(); Model * m_model; MouseOperationE m_operation; Qt::MouseButton m_activeButton; ViewDirectionE m_viewDirection; Matrix m_viewMatrix; Matrix m_invMatrix; double m_centerX; double m_centerY; double m_centerZ; double m_arcballPoint[3]; double m_rotX; double m_rotY; double m_rotZ; double m_width; double m_height; double m_zoomLevel; double m_unitWidth; double m_far; double m_near; double m_farOrtho; double m_nearOrtho; int m_viewportWidth; int m_viewportHeight; GLuint m_backgroundTexture; Texture * m_texture; std::string m_backgroundFile; QTimer * m_scrollTimer; bool m_inOverlay; ScrollButtonE m_overlayButton; GLuint m_scrollTextures[2]; bool m_capture; bool m_texturesLoaded; ViewOptionsE m_viewOptions; QPoint m_scrollStartPosition; QColor m_backColor; DecalList m_decals; Toolbox * m_toolbox; }; #endif // __MVIEWPORT_H mm3d-1.3.15/src/depui/textureframe.cc000066400000000000000000000076111466047437300174030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "textureframe.h" #include "texwidget.h" #include "texture.h" #include "model.h" #include "log.h" #include #include #include TextureFrame::TextureFrame( QWidget * parent ) : QFrame( parent ), m_materialId( -1 ), m_texture( NULL ), m_3d( false ), m_overrideSize( false ), m_overWidth( 1 ), m_overHeight( 1 ) { setFrameStyle( StyledPanel ); setFrameShadow( Sunken ); m_textureWidget= new TextureWidget( this ); } TextureFrame::~TextureFrame() { } void TextureFrame::setModel( Model * model ) { m_model = model; if ( m_textureWidget ) { m_textureWidget->setModel( model ); } } void TextureFrame::set3d( bool o ) { m_3d = o; if ( m_textureWidget ) { m_textureWidget->set3d( o ); updateSize(); } } void TextureFrame::resizeEvent( QResizeEvent * e ) { updateSize(); } void TextureFrame::textureChangedEvent( int materialId ) { m_materialId = materialId - 1; m_texture = m_model->getTextureData( m_materialId ); // Okay to pass null here m_textureWidget->setTexture( m_materialId, m_texture ); updateSize(); } void TextureFrame::resizeTexture( int width, int height ) { if ( m_texture || m_overrideSize ) { if ( m_3d ) { m_textureWidget->move( PAD_SIZE, PAD_SIZE ); m_textureWidget->resize( width, height ); } else { float x = 0.0; float y = 0.0; float scaleX = 1.0; float scaleY = 1.0; if ( m_overrideSize ) { x = m_overWidth; y = m_overHeight; } else { if ( m_texture ) { x = m_texture->m_origWidth; y = m_texture->m_origHeight; } else { x = 256.0; y = 256.0; } } scaleX = width / x; scaleY = height / y; if ( scaleX < scaleY ) { m_textureWidget->move( PAD_SIZE, (int) ((this->height() - (y * scaleX)) / 2) + PAD_SIZE ); m_textureWidget->resize( (int) (x * scaleX), (int) (y * scaleX) ); } else { m_textureWidget->move( (int) ((this->width() - (x * scaleY)) / 2) + PAD_SIZE, PAD_SIZE ); m_textureWidget->resize( (int) (x * scaleY), (int) (y * scaleY) ); } } } else { m_textureWidget->move( PAD_SIZE, PAD_SIZE ); m_textureWidget->resize( this->width() - PAD_SIZE * 2, this->height() - PAD_SIZE * 2); } } void TextureFrame::setTexture( int materialId, Texture * tex ) { m_materialId = materialId; m_texture = tex; m_textureWidget->setTexture( m_materialId, tex ); updateSize(); } void TextureFrame::updateSize() { resizeTexture( width() - PAD_SIZE*2, height() - PAD_SIZE*2 ); } void TextureFrame::sizeOverride( int width, int height ) { m_textureWidget->resize( 0, 0 ); m_overrideSize = true; m_overWidth = width; m_overHeight = height; updateSize(); } mm3d-1.3.15/src/depui/textureframe.h000066400000000000000000000036241466047437300172450ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TEXFRAME_H #define __TEXFRAME_H #include #include class Model; class TextureWidget; class Texture; class TextureFrame : public QFrame { Q_OBJECT public: TextureFrame( QWidget * parent = NULL ); virtual ~TextureFrame(); void setModel( Model * model ); void set3d( bool o ); void resizeTexture( int width, int height ); void updateSize(); void sizeOverride( int width, int height ); TextureWidget * getTextureWidget() { return m_textureWidget; }; void setTexture( int materialId, Texture * tex ); enum { PAD_SIZE = 6 }; public slots: void resizeEvent( QResizeEvent * e ); void textureChangedEvent( int textureId ); protected: TextureWidget * m_textureWidget; int m_materialId; Texture * m_texture; bool m_3d; bool m_overrideSize; int m_overWidth; int m_overHeight; Model * m_model; }; #endif // __TEXFRAME_H mm3d-1.3.15/src/depui/texwidget.cc000066400000000000000000001760211466047437300166760ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "texwidget.h" #include "texture.h" #include "model.h" #include "log.h" #include "mm3dport.h" #include #include #include #include #include #include #include #include "pixmap/arrow.xpm" #include "pixmap/crosshairrow.xpm" #define VP_ZOOMSCALE 0.75 static int const SCROLL_SIZE = 16; struct _ScrollButton_t { int x; int y; int texIndex; float s1; float t1; float s2; float t2; float s3; float t3; float s4; float t4; }; typedef struct _ScrollButton_t ScrollButtonT; static ScrollButtonT s_buttons[ TextureWidget::ScrollButtonMAX ] = { { -18, -18, 1, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }, // Pan { -52, -18, 0, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }, // Left { -35, -18, 0, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f }, // Right { -18, -35, 0, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }, // Up { -18, -52, 0, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }, // Down }; using std::vector; using std::list; TextureWidget::TextureWidget( QWidget * parent ) : QOpenGLWidget( parent ), m_sClamp( false ), m_tClamp( false ), m_zoom( 1.0 ), m_xCenter( 0.5 ), m_yCenter( 0.5 ), m_model( NULL ), m_materialId( -1 ), m_texture( NULL ), m_glTexture( 0 ), m_scrollTimer( new QTimer() ), m_overlayButton( ScrollButtonMAX ), m_drawMode( DM_Edit ), m_drawVertices( true ), m_drawBorder( false ), m_solidBackground( false ), m_operation( MouseSelect ), m_scaleKeepAspect( false ), m_scaleFromCenter( false ), m_selecting( false ), m_drawBounding( false ), m_drawRange( false ), m_interactive( false ), m_3d( false ), m_button( 0 ), m_animTimer( new QTimer() ), m_xMin( 0.0 ), m_xMax( 1.0 ), m_yMin( 0.0 ), m_yMax( 1.0 ), m_xRotPoint( 0.5 ), m_yRotPoint( 0.5 ), m_linesColor( 0xffffff ), m_selectionColor( 0xff0000 ) { connect( m_animTimer, SIGNAL(timeout()), this, SLOT(animationTimeout())); setFocusPolicy( Qt::WheelFocus ); connect( m_scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimeout())); setCursor( QCursor( Qt::ArrowCursor ) ); } TextureWidget::~TextureWidget() { m_animTimer->stop(); delete m_animTimer; m_animTimer = NULL; if ( isValid() ) { makeCurrent(); glDeleteTextures( 1, (GLuint *) &m_glTexture ); // Do NOT free m_texture. TextureManager does that. glDeleteTextures( 2, m_scrollTextures ); } m_scrollTimer->stop(); delete m_scrollTimer; } void TextureWidget::initializeGL() { if ( !isValid() ) { log_error( "texture widget does not have a valid OpenGL context\n" ); return; } // general set-up glEnable( GL_TEXTURE_2D ); glShadeModel( GL_SMOOTH ); glDepthFunc( GL_LEQUAL ); glClearColor( 0.80, 0.80, 0.80, 1.0 ); glClearDepth( 1.0f ); // set up texture if setTexture() was called before initializeGL() updateGLTexture(); // set up overlay arrows QPixmap arrow( arrow_xpm ); QPixmap cross( crosshairrow_xpm ); QImage img; glGenTextures( 2, m_scrollTextures ); img = arrow.toImage(); makeTextureFromImage( img, m_scrollTextures[0] ); img = cross.toImage(); makeTextureFromImage( img, m_scrollTextures[1] ); // set up lighting GLfloat ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat position[] = { 0.0f, 0.0f, 3.0f, 0.0f }; glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE ); glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); glLightfv( GL_LIGHT0, GL_POSITION, position ); glEnable( GL_LIGHT0 ); glEnable( GL_LIGHTING ); } void TextureWidget::resizeGL( int w, int h ) { if ( h == 0 ) h = 1; // FIXME?: I think Qt 5.6 (or later) changed resizeGL() behavior from // always being pixels to points that need to be scaled but I don't // have convient access to test old Qt versions. So using w and h or // scaling them should potentially have a Qt version check. m_viewportWidth = w * devicePixelRatioF(); m_viewportHeight = h * devicePixelRatioF(); updateViewport(); } void TextureWidget::updateViewport() { m_xMin = m_xCenter - (m_zoom / 2.0); m_xMax = m_xCenter + (m_zoom / 2.0); m_yMin = m_yCenter - (m_zoom / 2.0); m_yMax = m_yCenter + (m_zoom / 2.0); update(); } void TextureWidget::paintGL() { makeCurrent(); paintInternal(); } void TextureWidget::paintOnGlWidget( QOpenGLWidget * w ) { w->makeCurrent(); paintInternal(); } void TextureWidget::paintInternal() { setViewportDraw(); //log_debug( "paintInternal()\n" ); //log_debug( "(%f,%f) %f\n", m_xCenter, m_yCenter, m_zoom ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glLoadIdentity( ); glEnable( GL_LIGHTING ); if ( m_texture && !m_solidBackground ) { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, m_glTexture ); } else { glDisable( GL_TEXTURE_2D ); } glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); if ( m_solidBackground ) { glColor3f( 0.0f, 0.0f, 0.0f ); float fval[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; glMaterialfv( GL_FRONT, GL_AMBIENT, fval ); glMaterialfv( GL_FRONT, GL_DIFFUSE, fval ); glMaterialfv( GL_FRONT, GL_SPECULAR, fval ); glMaterialfv( GL_FRONT, GL_EMISSION, fval ); glMaterialf( GL_FRONT, GL_SHININESS, fval[0] ); } else if ( m_materialId >= 0 ) { glColor3f( 1.0f, 1.0f, 1.0f ); if ( m_model ) { float fval[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; m_model->getTextureAmbient( m_materialId, fval ); glMaterialfv( GL_FRONT, GL_AMBIENT, fval ); m_model->getTextureDiffuse( m_materialId, fval ); fval[3] = 1.0f; glMaterialfv( GL_FRONT, GL_DIFFUSE, fval ); m_model->getTextureSpecular( m_materialId, fval ); glMaterialfv( GL_FRONT, GL_SPECULAR, fval ); m_model->getTextureEmissive( m_materialId, fval ); glMaterialfv( GL_FRONT, GL_EMISSION, fval ); m_model->getTextureShininess( m_materialId, fval[0] ); glMaterialf( GL_FRONT, GL_SHININESS, fval[0] ); } else { float fval[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; glMaterialfv( GL_FRONT, GL_AMBIENT, fval ); fval[0] = fval[1] = fval[2] = 1.0f; glMaterialfv( GL_FRONT, GL_DIFFUSE, fval ); fval[0] = fval[1] = fval[2] = 0.0f; glMaterialfv( GL_FRONT, GL_SPECULAR, fval ); fval[0] = fval[1] = fval[2] = 0.0f; glMaterialfv( GL_FRONT, GL_EMISSION, fval ); glMaterialf( GL_FRONT, GL_SHININESS, 0.0f ); } } else { float fval[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; glMaterialfv( GL_FRONT, GL_AMBIENT, fval ); fval[0] = fval[1] = fval[2] = 1.0f; glMaterialfv( GL_FRONT, GL_DIFFUSE, fval ); fval[0] = fval[1] = fval[2] = 0.0f; glMaterialfv( GL_FRONT, GL_SPECULAR, fval ); fval[0] = fval[1] = fval[2] = 0.0f; glMaterialfv( GL_FRONT, GL_EMISSION, fval ); glMaterialf( GL_FRONT, GL_SHININESS, 0.0f ); if ( m_texture ) { glColor3f( 1.0f, 1.0f, 1.0f ); } else { glDisable( GL_LIGHTING ); glColor3f( 0.0f, 0.0f, 0.0f ); } } if ( m_materialId >= 0 && m_model && m_model->getMaterialType( m_materialId ) == Model::Material::MATTYPE_COLOR ) { GLubyte r = m_model->getMaterialColor( m_materialId, 0 ); GLubyte g = m_model->getMaterialColor( m_materialId, 1 ); GLubyte b = m_model->getMaterialColor( m_materialId, 2 ); glDisable( GL_TEXTURE_2D ); //glDisable( GL_LIGHTING ); glColor3ub( r, g, b ); } if ( m_materialId >= 0 && m_3d ) { glEnable( GL_DEPTH_TEST ); PORT_timeval tv; PORT_gettimeofday( &tv ); int ms = tv.tv_msec + (tv.tv_sec & 7) * 1000; float yRot = (float) ms / 4000.0 * 360.0; float xRot = (float) ms / 8000.0 * 360.0; glTranslatef( 0.0, 0.0, -5.0 ); glRotatef( yRot, 0.0, 1.0, 0.0 ); glRotatef( xRot, 1.0, 0.0, 0.0 ); glBegin( GL_QUADS ); // Front glTexCoord2f( 0.0, 0.0 ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( -1.0, -1.0, 1.0 ); glTexCoord2f( 1.0, 0.0 ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( 1.0, -1.0, 1.0 ); glTexCoord2f( 1.0, 1.0 ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( 1.0, 1.0, 1.0 ); glTexCoord2f( 0.0, 1.0 ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( -1.0, 1.0, 1.0 ); // Back glTexCoord2f( 0.0, 0.0 ); glNormal3f( 0.0, 0.0, -1.0 ); glVertex3f( 1.0, -1.0, -1.0 ); glTexCoord2f( 1.0, 0.0 ); glNormal3f( 0.0, 0.0, -1.0 ); glVertex3f( -1.0, -1.0, -1.0 ); glTexCoord2f( 1.0, 1.0 ); glNormal3f( 0.0, 0.0, -1.0 ); glVertex3f( -1.0, 1.0, -1.0 ); glTexCoord2f( 0.0, 1.0 ); glNormal3f( 0.0, 0.0, -1.0 ); glVertex3f( 1.0, 1.0, -1.0 ); // Left glTexCoord2f( 0.0, 0.0 ); glNormal3f( 1.0, 0.0, 0.0 ); glVertex3f( 1.0, -1.0, 1.0 ); glTexCoord2f( 1.0, 0.0 ); glNormal3f( 1.0, 0.0, 0.0 ); glVertex3f( 1.0, -1.0, -1.0 ); glTexCoord2f( 1.0, 1.0 ); glNormal3f( 1.0, 0.0, 0.0 ); glVertex3f( 1.0, 1.0, -1.0 ); glTexCoord2f( 0.0, 1.0 ); glNormal3f( 1.0, 0.0, 0.0 ); glVertex3f( 1.0, 1.0, 1.0 ); // Right glTexCoord2f( 0.0, 0.0 ); glNormal3f( -1.0, 0.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 ); glTexCoord2f( 1.0, 0.0 ); glNormal3f( -1.0, 0.0, 0.0 ); glVertex3f( -1.0, -1.0, 1.0 ); glTexCoord2f( 1.0, 1.0 ); glNormal3f( -1.0, 0.0, 0.0 ); glVertex3f( -1.0, 1.0, 1.0 ); glTexCoord2f( 0.0, 1.0 ); glNormal3f( -1.0, 0.0, 0.0 ); glVertex3f( -1.0, 1.0, -1.0 ); // Top glTexCoord2f( 0.0, 0.0 ); glNormal3f( 0.0, 1.0, 0.0 ); glVertex3f( -1.0, 1.0, -1.0 ); glTexCoord2f( 1.0, 0.0 ); glNormal3f( 0.0, 1.0, 0.0 ); glVertex3f( 1.0, 1.0, -1.0 ); glTexCoord2f( 1.0, 1.0 ); glNormal3f( 0.0, 1.0, 0.0 ); glVertex3f( 1.0, 1.0, 1.0 ); glTexCoord2f( 0.0, 1.0 ); glNormal3f( 0.0, 1.0, 0.0 ); glVertex3f( -1.0, 1.0, 1.0 ); // Bottom glTexCoord2f( 0.0, 0.0 ); glNormal3f( 0.0, -1.0, 0.0 ); glVertex3f( 1.0, -1.0, -1.0 ); glTexCoord2f( 1.0, 0.0 ); glNormal3f( 0.0, -1.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 ); glTexCoord2f( 1.0, 1.0 ); glNormal3f( 0.0, -1.0, 0.0 ); glVertex3f( -1.0, -1.0, 1.0 ); glTexCoord2f( 0.0, 1.0 ); glNormal3f( 0.0, -1.0, 0.0 ); glVertex3f( 1.0, -1.0, 1.0 ); glEnd(); glDisable( GL_DEPTH_TEST ); } else { glBegin( GL_QUADS ); glTexCoord2f( m_xMin, m_yMin ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( m_xMin, m_yMin, 0.0 ); glTexCoord2f( m_xMax, m_yMin ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( m_xMax, m_yMin, 0.0 ); glTexCoord2f( m_xMax, m_yMax ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( m_xMax, m_yMax, 0.0 ); glTexCoord2f( m_xMin, m_yMax ); glNormal3f( 0.0, 0.0, 1.0 ); glVertex3f( m_xMin, m_yMax, 0.0 ); glEnd(); } //glLoadIdentity( ); glDisable( GL_TEXTURE_2D ); glDisable( GL_LIGHTING ); glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); if ( m_drawBorder ) { glColor3f( 0.7, 0.7, 0.7 ); glBegin( GL_QUADS ); glVertex3f( 0.0f, 0.0f, -0.25f ); glVertex3f( 1.0f, 0.0f, -0.25f ); glVertex3f( 1.0f, 1.0f, -0.25f ); glVertex3f( 0.0f, 1.0f, -0.25f ); glEnd(); } useLinesColor(); if ( m_operation == MouseRange ) { glColor3f( 0.7, 0.7, 0.7 ); } switch ( m_drawMode ) { case DM_Edit: glBegin( GL_TRIANGLES ); drawTriangles(); glEnd(); break; case DM_Edges: glBegin( GL_TRIANGLES ); drawTriangles(); glEnd(); break; case DM_Filled: glColor3f( 0.0, 0.0, 0.8 ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glBegin( GL_TRIANGLES ); drawTriangles(); glEnd(); glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); break; case DM_FilledEdges: glColor3f( 0.0, 0.0, 0.8 ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glBegin( GL_TRIANGLES ); drawTriangles(); glEnd(); glClear( GL_DEPTH_BUFFER_BIT ); glColor3f( 1.0, 1.0, 1.0 ); glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); glBegin( GL_TRIANGLES ); drawTriangles(); glEnd(); break; default: log_error( "Unknown draw mode: %d\n", m_drawMode ); break; } // TODO may want to make "draw points" a separate property if ( m_operation == MouseRange ) { useLinesColor(); } // TODO may want to make "draw points" a separate property if ( m_operation != MouseRange || m_drawVertices ) { glPointSize( 3.0 * devicePixelRatioF() ); glBegin( GL_POINTS ); for ( unsigned t = 0; t < m_triangles.size(); t++ ) { for ( unsigned v = 0; v < 3; v++ ) { TextureVertexT * vert = m_vertices[ m_triangles[t]->vertex[v] ]; if ( m_drawMode == DM_Edit && vert->selected ) { useSelectionColor(); } else { useLinesColor(); } //glVertex3f( (vert->s - m_xMin) / m_zoom, (vert->t - m_yMin) / m_zoom, -0.5 ); glVertex3f( vert->s, vert->t, -0.5 ); } } glEnd(); } if ( m_drawBounding ) { drawSelectBox(); } if ( m_drawRange ) { drawRangeBox(); } if ( m_operation == MouseRotate ) { drawRotationPoint(); } if ( m_interactive ) { drawOverlay(); } } void TextureWidget::drawTriangles() { bool wrapLeft = false; bool wrapRight = false; bool wrapTop = false; bool wrapBottom = false; for ( unsigned t = 0; t < m_triangles.size(); t++ ) { TextureTriangleT * triangle = m_triangles[t]; wrapLeft = false; wrapRight = false; wrapTop = false; wrapBottom = false; for ( unsigned v = 0; v < 3; v++ ) { glVertex3f( m_vertices[ triangle->vertex[v] ]->s, m_vertices[ triangle->vertex[v] ]->t, -0.5 ); if ( m_drawMode != DM_Edit ) { if ( m_vertices[ triangle->vertex[v] ]->s < 0.0 ) { wrapLeft = true; } if ( m_vertices[ triangle->vertex[v] ]->s > 1.0 ) { wrapRight = true; } if ( m_vertices[ triangle->vertex[v] ]->t < 0.0 ) { wrapBottom = true; } if ( m_vertices[ triangle->vertex[v] ]->t > 1.0 ) { wrapTop = true; } } } if ( m_drawMode != DM_Edit ) { if ( wrapLeft ) { for ( unsigned v = 0; v < 3; v++ ) { glVertex3f( m_vertices[ triangle->vertex[v] ]->s + 1.0, m_vertices[ triangle->vertex[v] ]->t, -0.5 ); } } if ( wrapRight ) { for ( unsigned v = 0; v < 3; v++ ) { glVertex3f( m_vertices[ triangle->vertex[v] ]->s - 1.0, m_vertices[ triangle->vertex[v] ]->t, -0.5 ); } } if ( wrapBottom ) { for ( unsigned v = 0; v < 3; v++ ) { glVertex3f( m_vertices[ triangle->vertex[v] ]->s, m_vertices[ triangle->vertex[v] ]->t + 1.0, -0.5 ); } } if ( wrapTop ) { for ( unsigned v = 0; v < 3; v++ ) { glVertex3f( m_vertices[ triangle->vertex[v] ]->s, m_vertices[ triangle->vertex[v] ]->t - 1.0, -0.5 ); } } } } } void TextureWidget::animationTimeout() { update(); } void TextureWidget::setModel( Model * model ) { m_model = model; m_texture = NULL; clearCoordinates(); } void TextureWidget::set3d( bool o ) { m_3d = o; updateViewport(); if ( o ) { m_animTimer->start( 30 ); } else { m_animTimer->stop(); } } void TextureWidget::setInteractive( bool o ) { m_interactive = o; update(); } void TextureWidget::setTexture( int materialId, Texture * texture ) { m_materialId = materialId; m_texture = texture; m_sClamp = true; m_tClamp = true; if ( m_model ) { m_sClamp = m_model->getTextureSClamp( materialId ); m_tClamp = m_model->getTextureTClamp( materialId ); } m_zoom = 1.0; m_xCenter = 0.5; m_yCenter = 0.5; updateViewport(); if ( m_texture ) { /* m_xMin = m_xCenter - w; m_xMax = m_xCenter + w; m_yMin = m_yCenter - w; m_yMax = m_yCenter + w; */ } if ( !isValid() ) { // initializeGL() hasn't run yet return; } makeCurrent(); updateGLTexture(); resizeGL( this->width(), this->height() ); update(); } void TextureWidget::vFlipCoordinates() { for ( unsigned v = 0; v < m_vertices.size(); v++ ) { if ( m_vertices[v]->selected ) { m_vertices[v]->t = 1.0 - m_vertices[v]->t; } } update(); } void TextureWidget::hFlipCoordinates() { for ( unsigned v = 0; v < m_vertices.size(); v++ ) { if ( m_vertices[v]->selected ) { m_vertices[v]->s = 1.0 - m_vertices[v]->s; } } update(); } void TextureWidget::rotateCoordinatesCcw() { for ( unsigned v = 0; v < m_vertices.size(); v++ ) { if ( m_vertices[v]->selected ) { double temp = m_vertices[v]->t; m_vertices[v]->t = m_vertices[v]->s; m_vertices[v]->s = 1.0 - temp; } } update(); } void TextureWidget::rotateCoordinatesCw() { for ( unsigned v = 0; v < m_vertices.size(); v++ ) { if ( m_vertices[v]->selected ) { double temp = m_vertices[v]->t; m_vertices[v]->t = 1.0 - m_vertices[v]->s; m_vertices[v]->s = temp; } } update(); } int TextureWidget::addVertex( double s, double t ) { int index = m_vertices.size(); TextureVertexT * vert = new TextureVertexT; vert->s = s; vert->t = t; vert->selected = true; m_vertices.push_back( vert ); return index; } int TextureWidget::addTriangle( int v1, int v2, int v3 ) { int vCount = m_vertices.size(); if ( v1 >= 0 && v1 < vCount && v2 >= 0 && v2 < vCount && v3 >= 0 && v3 < vCount ) { int index = m_triangles.size(); TextureTriangleT * triangle = new TextureTriangleT; triangle->vertex[0] = v1; triangle->vertex[1] = v2; triangle->vertex[2] = v3; m_triangles.push_back( triangle ); return index; } return -1; } double TextureWidget::adjustToNearest( double angle ) { double f = angle / PIOVER180; // Change to degrees if ( f < 0.0 ) { int n = (int) (f / 15.0 - 0.5); f = n * 15.0; } else { int n = (int) (f / 15.0 + 0.5); f = n * 15.0; } log_debug( "nearest angle is %f\n", f ); return f * PIOVER180; } void TextureWidget::mousePressEvent( QMouseEvent * e ) { if ( m_interactive ) { int w = this->width(); int h = this->height(); m_lastXPos = e->pos().x(); m_lastYPos = e->pos().y(); m_button = m_button | e->button(); int bx = e->pos().x(); int by = h - e->pos().y(); m_overlayButton = ScrollButtonMAX; int sx = 0; int sy = 0; int size = SCROLL_SIZE; for ( int b = 0; m_overlayButton == ScrollButtonMAX && b < ScrollButtonMAX; b++ ) { sx = s_buttons[b].x; sy = s_buttons[b].y; if ( (bx >= w + sx) && (bx <= w + sx + size) && (by >= h + sy) && (by <= h + sy + size) ) { m_overlayButton = (ScrollButtonE) b; switch ( m_overlayButton ) { case ScrollButtonPan: break; case ScrollButtonUp: scrollUp(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; case ScrollButtonDown: scrollDown(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; case ScrollButtonLeft: scrollLeft(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; case ScrollButtonRight: scrollRight(); m_scrollTimer->setSingleShot( true ); m_scrollTimer->start( 300 ); break; default: break; } } } if ( m_overlayButton == ScrollButtonMAX ) { if ( e->button() & Qt::MidButton ) { // We're panning } else { switch ( m_operation ) { case MouseSelect: if ( !( (e->modifiers() & Qt::ShiftModifier) || (e->button() & Qt::RightButton) ) ) { clearSelected(); } m_xSel1 = (m_lastXPos / (double) this->width()) * (m_xMax - m_xMin) + m_xMin; m_ySel1 = (1.0 - (m_lastYPos / (double) this->height())) * (m_yMax - m_yMin) + m_yMin; m_selecting = ( e->button() & Qt::RightButton ) ? false : true; m_drawBounding = true; break; case MouseMove: m_allowX = true; m_allowY = true; break; case MouseScale: m_allowX = true; m_allowY = true; startScale( (m_lastXPos / (double) this->width()) * (m_xMax - m_xMin) + m_xMin , (1.0 - (m_lastYPos / (double) this->height())) * (m_yMax - m_yMin) + m_yMin ); break; case MouseRotate: { double aspect = (double) this->width() / (double) this->height(); if ( (e->button() & Qt::RightButton) ) { m_xRotPoint = (m_lastXPos / (double) this->width()) * (m_xMax - m_xMin) + m_xMin; m_yRotPoint = (1.0 - (m_lastYPos / (double) this->height())) * (m_yMax - m_yMin) + m_yMin; } else { m_xRotStart = (m_lastXPos / (double) this->width()) * (m_xMax - m_xMin) + m_xMin; m_yRotStart = (1.0 - (m_lastYPos / (double) this->height())) * (m_yMax - m_yMin) + m_yMin; m_xRotStart -= m_xRotPoint; m_yRotStart -= m_yRotPoint; double opposite = m_yRotStart; double adjacent = m_xRotStart * aspect; if ( adjacent < 0.0001 && adjacent > -0.0001 ) { adjacent = (adjacent >= 0 ) ? 0.0001 : -0.0001; } double angle = atan( opposite / adjacent ); float quad = PIOVER180 * 90; if ( adjacent < 0 ) { if ( opposite < 0 ) { angle = -(quad) - ( (quad) - angle ); } else { angle = (quad) + ( (quad) + angle ); } } if ( e->modifiers() & Qt::ShiftModifier ) { angle = adjustToNearest( angle ); } m_startAngle = angle; } freeRotateVertices(); for ( unsigned v = 0; v < m_vertices.size(); v++ ) { if ( m_vertices[v]->selected ) { RotateVertexT * rot = new RotateVertexT; rot->x = (m_vertices[v]->s - m_xRotPoint) * aspect; rot->y = m_vertices[v]->t - m_yRotPoint; rot->v = v; m_rotateVertices.push_back( rot ); } } update(); } break; case MouseRange: { m_dragAll = false; m_dragTop = false; m_dragBottom = false; m_dragLeft = false; m_dragRight = false; double windowX = getWindowXCoord( m_lastXPos ); double windowY = getWindowYCoord( m_lastYPos ); getDragDirections( windowX, windowY, m_dragAll, m_dragTop, m_dragBottom, m_dragLeft, m_dragRight ); setDragCursor( m_dragAll, m_dragTop, m_dragBottom, m_dragLeft, m_dragRight ); } break; default: log_error( "Unknown mouse operation: %d\n", m_operation ); break; } } } } } void TextureWidget::mouseReleaseEvent( QMouseEvent * e ) { if ( m_interactive ) { if ( m_overlayButton == ScrollButtonMAX ) { int x = e->pos().x(); int y = e->pos().y(); if ( e->button() & Qt::MidButton ) { // We're panning } else { switch ( m_operation ) { case MouseSelect: m_drawBounding = false; updateSelectRegion( (x / (double) this->width()) * (m_xMax - m_xMin) + m_xMin, (1.0 - (y / (double) this->height())) * (m_yMax - m_yMin) + m_yMin ); selectDone(); emit updateSelectionDoneSignal(); break; case MouseMove: if ( e->modifiers() & Qt::ShiftModifier ) { if ( m_allowX && m_allowY ) { double ax = fabs( x - m_lastXPos ); double ay = fabs( y - m_lastYPos ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { x = m_lastXPos; } if ( !m_allowY ) { y = m_lastYPos; } moveSelectedVertices( ((x - m_lastXPos) / (double) this->width()) * (m_xMax - m_xMin), (-(y - m_lastYPos) / (double) this->height()) * (m_yMax - m_yMin) ); emit updateCoordinatesSignal(); emit updateCoordinatesDoneSignal(); break; case MouseRotate: // Nothing to do here emit updateCoordinatesDoneSignal(); break; case MouseScale: // Nothing to do here emit updateCoordinatesDoneSignal(); break; case MouseRange: if ( m_button & Qt::LeftButton ) { if ( m_dragAll || m_dragTop || m_dragBottom || m_dragLeft || m_dragRight ) { emit updateRangeDoneSignal(); } } else { if ( m_dragAll ) { emit updateSeamDoneSignal(); } } break; default: log_error( "Unknown mouse operation: %d\n", m_operation ); break; } } } else { m_overlayButton = ScrollButtonMAX; m_scrollTimer->stop(); } m_button = m_button & ~(e->button()); } } void TextureWidget::mouseMoveEvent( QMouseEvent * e ) { if ( m_interactive ) { int x = e->pos().x(); int y = e->pos().y(); if ( m_overlayButton == ScrollButtonMAX ) { if ( m_button != 0 ) { if ( m_button & Qt::MidButton ) { double xDiff = (double) -(x - m_lastXPos); double yDiff = (double) (y - m_lastYPos); xDiff = xDiff / (double) this->width(); yDiff = yDiff / (double) this->height(); xDiff *= m_zoom; yDiff *= m_zoom; m_xCenter += xDiff; m_yCenter += yDiff; m_xMin += xDiff; m_yMin += yDiff; m_xMax += xDiff; m_yMax += yDiff; updateViewport(); } else { switch ( m_operation ) { case MouseSelect: /* updateSelectRegion( x / (double) this->width(), 1.0 - (y / (double) this->height()) ); */ updateSelectRegion( (x / (double) this->width()) * m_zoom + m_xMin, (1.0 - (y / (double) this->height())) * m_zoom + m_yMin ); break; case MouseMove: if ( e->modifiers() & Qt::ShiftModifier ) { if ( m_allowX && m_allowY ) { double ax = fabs( x - m_lastXPos ); double ay = fabs( y - m_lastYPos ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { x = m_lastXPos; } if ( !m_allowY ) { y = m_lastYPos; } moveSelectedVertices( ((x - m_lastXPos) / (double) this->width()) * m_zoom, (-(y - m_lastYPos) / (double) this->height()) * m_zoom); emit updateCoordinatesSignal(); break; case MouseRotate: { double xNew = (x / (double) this->width()) * (m_xMax - m_xMin) + m_xMin; double yNew = (1.0 - (y / (double) this->height())) * (m_yMax - m_yMin) + m_yMin; xNew -= m_xRotPoint; yNew -= m_yRotPoint; double aspect = (double) this->width() / (double) this->height(); double opposite = yNew; double adjacent = xNew * aspect; if ( adjacent < 0.0001 && adjacent > -0.0001 ) { adjacent = (adjacent >= 0 ) ? 0.0001 : -0.0001; } double angle = atan( opposite / adjacent ); float quad = PIOVER180 * 90; if ( adjacent < 0 ) { if ( opposite < 0 ) { angle = -(quad) - ( (quad) - angle ); } else { angle = (quad) + ( (quad) + angle ); } } if ( e->modifiers() & Qt::ShiftModifier ) { angle = adjustToNearest( angle ); } rotateSelectedVertices( angle - m_startAngle ); emit updateCoordinatesSignal(); } break; case MouseScale: if ( e->modifiers() & Qt::ShiftModifier ) { if ( m_allowX && m_allowY ) { double ax = fabs( x - m_lastXPos ); double ay = fabs( y - m_lastYPos ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { x = m_lastXPos; } if ( !m_allowY ) { y = m_lastYPos; } scaleSelectedVertices( (x / (double) this->width()) * m_zoom + m_xMin, (1.0 - (y / (double) this->height())) * m_zoom + m_yMin ); emit updateCoordinatesSignal(); break; case MouseRange: if ( m_button & Qt::LeftButton ) { double xThen = getWindowXCoord( m_lastXPos ); double xNow = getWindowXCoord( x ); double yThen = getWindowYCoord( m_lastYPos ); double yNow = getWindowYCoord( y ); double xDiff = xNow - xThen; double yDiff = yNow - yThen; if ( m_dragLeft || m_dragAll ) { m_xRangeMin += xDiff; if ( m_xRangeMin > m_xRangeMax ) { m_xRangeMax = m_xRangeMin; } } if ( m_dragRight || m_dragAll ) { m_xRangeMax += xDiff; if ( m_xRangeMax < m_xRangeMin ) { m_xRangeMin = m_xRangeMax; } } if ( m_dragBottom || m_dragAll ) { m_yRangeMin += yDiff; if ( m_yRangeMin > m_yRangeMax ) { m_yRangeMax = m_yRangeMin; } } if ( m_dragTop || m_dragAll ) { m_yRangeMax += yDiff; if ( m_yRangeMax < m_yRangeMin ) { m_yRangeMin = m_yRangeMax; } } if ( m_dragAll || m_dragTop || m_dragBottom || m_dragLeft || m_dragRight ) { emit updateRangeSignal(); } } else { if ( m_dragAll ) { double xThen = getWindowXCoord( m_lastXPos ); double xNow = getWindowXCoord( x ); double yThen = getWindowYCoord( m_lastYPos ); double yNow = getWindowYCoord( y ); double xDiff = xNow - xThen; double yDiff = yNow - yThen; xDiff *= -(2 * PI); yDiff *= -(2 * PI); emit updateSeamSignal( xDiff, yDiff ); } } break; default: log_error( "Unknown mouse operation: %d\n", m_operation ); break; } } } else { updateCursorShape( x, y ); } } else { if ( m_overlayButton == ScrollButtonPan ) { double xDiff = (double) -(x - m_lastXPos); double yDiff = (double) (y - m_lastYPos); xDiff = xDiff / (double) this->width(); yDiff = yDiff / (double) this->height(); xDiff *= m_zoom; yDiff *= m_zoom; m_xCenter += xDiff; m_yCenter += yDiff; m_xMin += xDiff; m_yMin += yDiff; m_xMax += xDiff; m_yMax += yDiff; updateViewport(); } } m_lastXPos = x; m_lastYPos = y; } } void TextureWidget::wheelEvent( QWheelEvent * e ) { if ( m_interactive ) { if ( e->angleDelta().y() > 0 ) { zoomIn(); } else if ( e->angleDelta().y() < 0 ) { zoomOut(); } } } void TextureWidget::keyPressEvent( QKeyEvent * e ) { if ( m_interactive ) { switch ( e->key() ) { case Qt::Key_Home: { if ( (e->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier ) { if ( m_drawMode == DM_Edit ) { if ( m_operation == MouseRange ) { m_xCenter = (m_xRangeMax - m_xRangeMin) / 2.0 + m_xRangeMin; m_yCenter = (m_yRangeMax - m_yRangeMin) / 2.0 + m_yRangeMin; double xzoom = m_xRangeMax - m_xRangeMin; double yzoom = m_yRangeMax - m_yRangeMin; m_zoom = (xzoom > yzoom) ? xzoom : yzoom; m_zoom *= 1.10; updateViewport(); } else { bool first = true; double xMin = 0.0; double xMax = 0.0; double yMin = 0.0; double yMax = 0.0; size_t vcount = m_vertices.size(); for ( size_t v = 1; v < vcount; v++ ) { if ( m_vertices[v]->selected ) { if ( first ) { xMin = m_vertices[v]->s; yMin = m_vertices[v]->t; first = false; } else { if ( m_vertices[v]->s < xMin ) xMin = m_vertices[v]->s; if ( m_vertices[v]->s > xMax ) xMax = m_vertices[v]->s; if ( m_vertices[v]->t < yMin ) yMin = m_vertices[v]->t; if ( m_vertices[v]->t > yMax ) yMax = m_vertices[v]->t; } } } if ( !first ) { m_xCenter = (xMax - xMin) / 2.0 + xMin; m_yCenter = (yMax - yMin) / 2.0 + yMin; double xzoom = xMax - xMin; double yzoom = yMax - yMin; m_zoom = (xzoom > yzoom) ? xzoom : yzoom; m_zoom *= 1.10; updateViewport(); } } } } else { m_xCenter = 0.5; m_yCenter = 0.5; m_zoom = 1.0; updateViewport(); } } break; case Qt::Key_Equal: case Qt::Key_Plus: { zoomIn(); } break; case Qt::Key_Minus: case Qt::Key_Underscore: { zoomOut(); } break; case Qt::Key_0: m_xCenter = 0.5; m_yCenter = 0.5; updateViewport(); break; case Qt::Key_Up: scrollUp(); break; case Qt::Key_Down: scrollDown(); break; case Qt::Key_Left: scrollLeft(); break; case Qt::Key_Right: scrollRight(); break; default: QOpenGLWidget::keyPressEvent( e ); break; } } else { QOpenGLWidget::keyPressEvent( e ); } } void TextureWidget::zoomIn() { if ( m_interactive ) { if ( (m_zoom / VP_ZOOMSCALE) > 0.0001 ) { m_zoom *= (VP_ZOOMSCALE); } QString zoomStr = QString::asprintf( "%f", (float) m_zoom ); emit zoomLevelChanged( zoomStr ); updateViewport(); } } void TextureWidget::zoomOut() { if ( m_interactive ) { if ( (m_zoom / VP_ZOOMSCALE) < 250000 ) { m_zoom /= VP_ZOOMSCALE; } QString zoomStr = QString::asprintf( "%f", (float) m_zoom ); emit zoomLevelChanged( zoomStr ); updateViewport(); } } void TextureWidget::setZoomLevel( double zoom ) { if ( m_interactive ) { m_zoom = zoom; updateViewport(); } } void TextureWidget::scrollTimeout() { switch ( m_overlayButton ) { case ScrollButtonUp: scrollUp(); break; case ScrollButtonDown: scrollDown(); break; case ScrollButtonLeft: scrollLeft(); break; case ScrollButtonRight: scrollRight(); break; default: m_scrollTimer->stop(); return; } m_scrollTimer->setSingleShot( false ); m_scrollTimer->start( 100 ); } void TextureWidget::scrollUp() { m_yCenter += m_zoom * 0.10; updateViewport(); } void TextureWidget::scrollDown() { m_yCenter -= m_zoom * 0.10; updateViewport(); } void TextureWidget::scrollLeft() { m_xCenter -= m_zoom * 0.10; updateViewport(); } void TextureWidget::scrollRight() { m_xCenter += m_zoom * 0.10; updateViewport(); } void TextureWidget::moveSelectedVertices( double x, double y ) { for ( unsigned t = 0; t < m_vertices.size(); t++ ) { if ( m_vertices[t]->selected ) { m_vertices[t]->s += x; m_vertices[t]->t += y; } } update(); } void TextureWidget::updateSelectRegion( double x, double y ) { m_xSel2 = x; m_ySel2 = y; update(); } void TextureWidget::setViewportDraw() { glViewport( 0, 0, ( GLint ) m_viewportWidth, ( GLint ) m_viewportHeight ); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); GLfloat ratio = ( GLfloat ) m_viewportWidth / ( GLfloat ) m_viewportHeight; glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); if ( m_3d ) { gluPerspective( 45.0f, ratio, 0.01, 30.0 ); } else { glOrtho( m_xMin, m_xMax, m_yMin, m_yMax, -1.0, 1.0 ); } glMatrixMode( GL_MODELVIEW ); glLoadIdentity( ); } void TextureWidget::setViewportOverlay() { glViewport( 0, 0, ( GLint ) m_viewportWidth, ( GLint ) m_viewportHeight ); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); glOrtho( 0, this->width(), 0, this->height(), -1.0, 1.0 ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity( ); } void TextureWidget::drawOverlay() { setViewportOverlay(); glDisable( GL_LIGHTING ); glColor3f( 1.0f, 1.0f, 1.0f ); glEnable( GL_TEXTURE_2D ); int w = this->width(); int h = this->height(); int sx = 0; int sy = 0; int size = SCROLL_SIZE; for ( int b = 0; b < ScrollButtonMAX; b++ ) { ScrollButtonT * sbt = &s_buttons[b]; sx = sbt->x; sy = sbt->y; glBindTexture( GL_TEXTURE_2D, m_scrollTextures[ sbt->texIndex ] ); glBegin( GL_QUADS ); glTexCoord2f( sbt->s1, sbt->t1 ); glVertex3f( w + sx, h + sy, 0 ); glTexCoord2f( sbt->s2, sbt->t2 ); glVertex3f( w + sx + size, h + sy, 0 ); glTexCoord2f( sbt->s3, sbt->t3 ); glVertex3f( w + sx + size, h + sy + size, 0 ); glTexCoord2f( sbt->s4, sbt->t4 ); glVertex3f( w + sx, h + sy + size, 0 ); glEnd(); } glDisable( GL_TEXTURE_2D ); } void TextureWidget::makeTextureFromImage( const QImage & i, GLuint & t ) { glBindTexture( GL_TEXTURE_2D, t ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); int w = i.width(); int h = i.height(); unsigned pixelCount = w * i.height(); uint8_t * data = new uint8_t[ pixelCount * 3 ]; for ( int y = 0; y < h; y ++ ) { for ( int x = 0; x < w; x++ ) { QRgb p = i.pixel( x, h - y - 1 ); data[ ((y * w + x)*3) + 0 ] = qRed( p ); data[ ((y * w + x)*3) + 1 ] = qGreen( p ); data[ ((y * w + x)*3) + 2 ] = qBlue( p ); } } gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, w, h, GL_RGB, GL_UNSIGNED_BYTE, data ); delete[] data; } void TextureWidget::updateGLTexture() { if ( !m_texture ) { return; } glEnable( GL_TEXTURE_2D ); if ( m_glTexture == 0 ) { glGenTextures( 1, &m_glTexture ); } glBindTexture( GL_TEXTURE_2D, m_glTexture ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (m_sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT) ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (m_tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT) ); GLuint format = m_texture->m_format == Texture::FORMAT_RGBA ? GL_RGBA : GL_RGB; gluBuild2DMipmaps( GL_TEXTURE_2D, format, m_texture->m_width, m_texture->m_height, format, GL_UNSIGNED_BYTE, m_texture->m_data ); } void TextureWidget::selectDone() { if ( m_xSel1 > m_xSel2 ) { double temp = m_xSel2; m_xSel2 = m_xSel1; m_xSel1 = temp; } if ( m_ySel1 > m_ySel2 ) { double temp = m_ySel2; m_ySel2 = m_ySel1; m_ySel1 = temp; } for ( unsigned v = 0; v < m_vertices.size(); v++ ) { if ( m_vertices[v]->s >= m_xSel1 && m_vertices[v]->s <= m_xSel2 && m_vertices[v]->t >= m_ySel1 && m_vertices[v]->t <= m_ySel2 ) { m_vertices[v]->selected = m_selecting; } } update(); } void TextureWidget::setTextureCount( unsigned c ) { setTexture( m_materialId, m_texture ); } void TextureWidget::setMouseOperation( MouseOperationE op ) { m_operation = op; m_drawRange = false; if ( op == MouseRange ) { m_drawRange = true; } } void TextureWidget::setDrawMode( DrawModeE dm ) { m_drawMode = dm; } void TextureWidget::drawSelectBox() { glLogicOp( GL_XOR ); glEnable( GL_COLOR_LOGIC_OP ); glLineWidth( devicePixelRatioF() ); glColor3f( 1.0, 1.0, 1.0 ); glBegin( GL_LINES ); glVertex3f( m_xSel1, m_ySel1, -0.75 ); glVertex3f( m_xSel1, m_ySel2, -0.75 ); glVertex3f( m_xSel1, m_ySel2, -0.75 ); glVertex3f( m_xSel2, m_ySel2, -0.75 ); glVertex3f( m_xSel2, m_ySel2, -0.75 ); glVertex3f( m_xSel2, m_ySel1, -0.75 ); glVertex3f( m_xSel2, m_ySel1, -0.75 ); glVertex3f( m_xSel1, m_ySel1, -0.75 ); glEnd(); glLogicOp( GL_COPY ); glDisable( GL_LOGIC_OP ); } void TextureWidget::drawRangeBox() { glLogicOp( GL_COPY ); glDisable( GL_LOGIC_OP ); glLineWidth( devicePixelRatioF() ); glColor3f( 1.0, 1.0, 1.0 ); glBegin( GL_LINES ); glVertex3f( m_xRangeMin, m_yRangeMin, -0.95 ); glVertex3f( m_xRangeMin, m_yRangeMax, -0.95 ); glVertex3f( m_xRangeMin, m_yRangeMax, -0.95 ); glVertex3f( m_xRangeMax, m_yRangeMax, -0.95 ); glVertex3f( m_xRangeMax, m_yRangeMax, -0.95 ); glVertex3f( m_xRangeMax, m_yRangeMin, -0.95 ); glVertex3f( m_xRangeMax, m_yRangeMin, -0.95 ); glVertex3f( m_xRangeMin, m_yRangeMin, -0.95 ); glEnd(); } void TextureWidget::drawRotationPoint() { glLogicOp( GL_COPY ); glDisable( GL_LOGIC_OP ); glLineWidth( devicePixelRatioF() ); glColor3f( 0.0, 1.0, 0.0 ); glBegin( GL_LINES ); double offset = m_zoom * 0.04; double aspect = (double) this->width() / (double) this->height(); double xoff = offset / aspect; double yoff = offset; glVertex3f( m_xRotPoint - xoff, m_yRotPoint + 0.0, -0.95 ); glVertex3f( m_xRotPoint + 0.0, m_yRotPoint - yoff, -0.95 ); glVertex3f( m_xRotPoint + 0.0, m_yRotPoint - yoff, -0.95 ); glVertex3f( m_xRotPoint + xoff, m_yRotPoint + 0.0, -0.95 ); glVertex3f( m_xRotPoint + xoff, m_yRotPoint + 0.0, -0.95 ); glVertex3f( m_xRotPoint + 0.0, m_yRotPoint + yoff, -0.95 ); glVertex3f( m_xRotPoint + 0.0, m_yRotPoint + yoff, -0.95 ); glVertex3f( m_xRotPoint - xoff, m_yRotPoint + 0.0, -0.95 ); glEnd(); } void TextureWidget::clearSelected() { for ( unsigned v = 0; v < m_vertices.size(); v++ ) { m_vertices[v]->selected = false; } } double TextureWidget::getWindowXCoord( int x ) { return (x / (double) m_viewportWidth) * m_zoom + m_xMin; } double TextureWidget::getWindowYCoord( int y ) { return ((m_viewportHeight - y) / (double) m_viewportHeight) * m_zoom + m_yMin; } void TextureWidget::updateCursorShape( int x, int y ) { if ( m_interactive ) { int w = this->width(); int h = this->height(); int sx = 0; int sy = 0; int size = SCROLL_SIZE; int bx = x; int by = h - y; ScrollButtonE button = ScrollButtonMAX; for ( int b = 0; button == ScrollButtonMAX && b < ScrollButtonMAX; b++ ) { sx = s_buttons[b].x; sy = s_buttons[b].y; if ( (bx >= w + sx) && (bx <= w + sx + size) && (by >= h + sy) && (by <= h + sy + size) ) { button = (ScrollButtonE) b; } } switch ( button ) { case ScrollButtonPan: case ScrollButtonUp: case ScrollButtonDown: case ScrollButtonLeft: case ScrollButtonRight: break; default: break; } if ( button == ScrollButtonMAX ) { if ( m_operation == MouseRange ) { bool dragAll = false; bool dragTop = false; bool dragBottom = false; bool dragLeft = false; bool dragRight = false; double windowX = getWindowXCoord( x ); double windowY = getWindowYCoord( y ); getDragDirections( windowX, windowY, dragAll, dragTop, dragBottom, dragLeft, dragRight ); setDragCursor( dragAll, dragTop, dragBottom, dragLeft, dragRight ); return; } } } setDragCursor( false, false, false, false, false ); } void TextureWidget::getDragDirections( double windowX, double windowY, bool & dragAll, bool & dragTop, bool & dragBottom, bool & dragLeft, bool & dragRight ) { dragAll = false; dragTop = false; dragBottom = false; dragLeft = false; dragRight = false; double prox = (6.0 / this->width()) * m_zoom; if ( (windowX >= (m_xRangeMin - prox)) && (windowX <= (m_xRangeMax + prox)) && (windowY >= (m_yRangeMin - prox)) && (windowY <= (m_yRangeMax + prox)) ) { if ( fabs(m_xRangeMin - windowX) <= prox ) { dragLeft = true; } if ( fabs(m_xRangeMax - windowX) <= prox ) { dragRight = true; } if ( fabs(m_yRangeMin - windowY) <= prox ) { dragBottom = true; } if ( fabs(m_yRangeMax - windowY) <= prox ) { dragTop = true; } if ( dragLeft && dragRight ) { // The min and max are very close together, don't drag // both at one time. if ( windowX < m_xRangeMin ) { dragRight = false; } else if ( windowX > m_xRangeMax ) { dragLeft = false; } else { // We're in-between, don't drag either (top/bottom still okay) dragLeft = false; dragRight = false; } } if ( dragTop && dragBottom ) { // The min and max are very close together, don't drag // both at one time. if ( windowY < m_yRangeMin ) { dragTop = false; } else if ( windowY > m_yRangeMax ) { dragBottom = false; } else { // We're in-between, don't drag either (left/right still okay) dragTop = false; dragBottom = false; } } if ( !dragTop && !dragBottom && !dragLeft && !dragRight ) { if ( (windowX > m_xRangeMin && windowX < m_xRangeMax) && (windowY > m_yRangeMin && windowY < m_yRangeMax ) ) { dragAll = true; } } } } void TextureWidget::setDragCursor( bool dragAll, bool dragTop, bool dragBottom, bool dragLeft, bool dragRight ) { if ( !m_interactive ) { setCursor( QCursor( Qt::ArrowCursor ) ); } else if ( (dragLeft && dragTop) || (dragRight && dragBottom) ) { setCursor( QCursor( Qt::SizeFDiagCursor ) ); } else if ( (dragLeft && dragBottom) || (dragRight && dragTop) ) { setCursor( QCursor( Qt::SizeBDiagCursor ) ); } else if ( dragLeft || dragRight ) { setCursor( QCursor( Qt::SizeHorCursor ) ); } else if ( dragTop || dragBottom ) { setCursor( QCursor( Qt::SizeVerCursor ) ); } else if ( dragAll ) { setCursor( QCursor( Qt::SizeAllCursor ) ); } else { setCursor( QCursor( Qt::ArrowCursor ) ); } } void TextureWidget::clearCoordinates() { while ( m_triangles.size() ) { delete m_triangles.back(); m_triangles.pop_back(); } while ( m_vertices.size() ) { delete m_vertices.back(); m_vertices.pop_back(); } freeRotateVertices(); } void TextureWidget::getCoordinates( int tri, float * s, float * t ) { if ( !t || !s || tri >= (signed) m_triangles.size() ) { return; } for ( int v = 0; v < 3; v++ ) { s[v] = m_vertices[ m_triangles[ tri ]->vertex[v] ]->s; t[v] = m_vertices[ m_triangles[ tri ]->vertex[v] ]->t; } } void TextureWidget::saveSelectedUv() { std::vector selectedUv; for ( size_t vert = 0; vert < m_vertices.size(); ++vert ) { if ( m_vertices[ vert ]->selected ) selectedUv.push_back( vert ); } m_model->setSelectedUv( selectedUv ); } void TextureWidget::restoreSelectedUv() { std::vector selectedUv; m_model->getSelectedUv( selectedUv ); for ( size_t vert = 0; vert < m_vertices.size(); ++vert ) { m_vertices[ vert ]->selected = false; } for ( size_t vert = 0; vert < selectedUv.size(); ++vert ) { size_t v = selectedUv[vert]; if ( v < m_vertices.size() ) m_vertices[ v ]->selected = true; } } void TextureWidget::setRange( double xMin, double yMin, double xMax, double yMax ) { m_xRangeMin = xMin; m_yRangeMin = yMin; m_xRangeMax = xMax; m_yRangeMax = yMax; } void TextureWidget::getRange( double & xMin, double & yMin, double & xMax, double & yMax ) { xMin = m_xRangeMin; yMin = m_yRangeMin; xMax = m_xRangeMax; yMax = m_yRangeMax; } void TextureWidget::startScale( double x, double y ) { m_scaleList.clear(); bool first = true; double minX = 0; double minY = 0; double maxX = 0; double maxY = 0; for ( unsigned t = 0; t < m_vertices.size(); t++ ) { if ( m_vertices[t]->selected ) { // update range if ( first ) { minX = m_vertices[t]->s; minY = m_vertices[t]->t; maxX = m_vertices[t]->s; maxY = m_vertices[t]->t; first = false; } else { if ( m_vertices[t]->s < minX ) { minX = m_vertices[t]->s; }; if ( m_vertices[t]->t < minY ) { minY = m_vertices[t]->t; }; if ( m_vertices[t]->s > maxX ) { maxX = m_vertices[t]->s; }; if ( m_vertices[t]->t > maxY ) { maxY = m_vertices[t]->t; }; } ScaleVerticesT sv; sv.index = t; sv.x = m_vertices[t]->s; sv.y = m_vertices[t]->t; m_scaleList.push_back( sv ); } } if ( m_scaleFromCenter ) { m_centerX = (maxX - minX) / 2.0 + minX; m_centerY = (maxY - minY) / 2.0 + minY; m_startLengthX = fabs( m_centerX - x ); m_startLengthY = fabs( m_centerY - y ); } else { double minmin = distance( x, y, minX, minY ); double minmax = distance( x, y, minX, maxY ); double maxmin = distance( x, y, maxX, minY ); double maxmax = distance( x, y, maxX, maxY ); if ( minmin > minmax ) { if ( minmin > maxmin ) { if ( minmin > maxmax ) { m_farX = minX; m_farY = minY; } else { m_farX = maxX; m_farY = maxY; } } else // maxmin > minmin { if ( maxmin > maxmax ) { m_farX = maxX; m_farY = minY; } else { m_farX = maxX; m_farY = maxY; } } } else // minmax > minmin { if ( minmax > maxmin ) { if ( minmax > maxmax ) { m_farX = minX; m_farY = maxY; } else { m_farX = maxX; m_farY = maxY; } } else // maxmin > minmax { if ( maxmin > maxmax ) { m_farX = maxX; m_farY = minY; } else { m_farX = maxX; m_farY = maxY; } } } m_startLengthX = fabs( x - m_farX ); m_startLengthY = fabs( y - m_farY ); } } void TextureWidget::rotateSelectedVertices( double angle ) { Matrix m; Vector rot; rot[0] = 0.0; rot[1] = 0.0; rot[2] = angle; m.setRotation( rot ); double aspect = (double) this->width() / (double) this->height(); unsigned vcount = m_rotateVertices.size(); for ( unsigned v = 0; v < vcount; v++ ) { Vector vec; vec[0] = m_rotateVertices[v]->x; vec[1] = m_rotateVertices[v]->y; m.apply3( vec ); unsigned index = m_rotateVertices[v]->v; m_vertices[index]->s = (vec[0] / aspect)+ m_xRotPoint; m_vertices[index]->t = vec[1] + m_yRotPoint; } update(); } void TextureWidget::scaleSelectedVertices( double x, double y ) { double spX = m_scaleFromCenter ? m_centerX : m_farX; double spY = m_scaleFromCenter ? m_centerY : m_farY; double lengthX = fabs( x - spX ); double lengthY = fabs( y - spY ); ScaleVerticesList::iterator it; for( it = m_scaleList.begin(); it != m_scaleList.end(); it++ ) { double x = (*it).x; double y = (*it).y; x -= spX; y -= spY; double xper = (lengthX / m_startLengthX); if ( m_startLengthX <= 0.00006 ) { xper = 1.0; } double yper = (lengthY / m_startLengthY); if ( m_startLengthY <= 0.00006 ) { yper = 1.0; } if ( m_scaleKeepAspect ) { if ( xper > yper ) { yper = xper; } else { xper = yper; } } x *= xper; y *= yper; x += spX; y += spY; m_vertices[(*it).index]->s = x; m_vertices[(*it).index]->t = y; } update(); } void TextureWidget::freeRotateVertices() { while ( m_rotateVertices.size() ) { delete m_rotateVertices.back(); m_rotateVertices.pop_back(); } } double TextureWidget::distance( const double & x1, const double & y1, const double & x2, const double & y2 ) { double xDiff = x2 - x1; double yDiff = y2 - y1; return sqrt( xDiff*xDiff + yDiff*yDiff ); } double TextureWidget::max( const double & a, const double & b ) { return ( a > b ) ? a : b; } void TextureWidget::useLinesColor() { float b = ((m_linesColor >> 0) & 255) / 255.0; float g = ((m_linesColor >> 8) & 255) / 255.0; float r = ((m_linesColor >> 16) & 255) / 255.0; glColor3f( r, g, b ); } void TextureWidget::useSelectionColor() { float b = ((m_selectionColor >> 0) & 255) / 255.0; float g = ((m_selectionColor >> 8) & 255) / 255.0; float r = ((m_selectionColor >> 16) & 255) / 255.0; glColor3f( r, g, b ); } mm3d-1.3.15/src/depui/texwidget.h000066400000000000000000000215601466047437300165350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TEXWIDGET_H #define __TEXWIDGET_H #include #include #include #include #include #include #include #include class Texture; class Model; class QTimer; class TextureWidget : public QOpenGLWidget { Q_OBJECT public: enum _DrawMode_e { DM_Edit, DM_Edges, DM_Filled, DM_FilledEdges, DM_MAX }; typedef enum _DrawMode_e DrawModeE; enum _MouseOperation_e { MouseMove, MouseSelect, MouseScale, MouseRotate, MouseRange }; typedef enum _MouseOperation_e MouseOperationE; enum _ScrollButton_e { ScrollButtonPan, ScrollButtonLeft, ScrollButtonRight, ScrollButtonUp, ScrollButtonDown, ScrollButtonMAX }; typedef enum _ScrollButton_e ScrollButtonE; struct _TextureVertex_t { double s; double t; bool selected; }; struct _RotateVertex_t { unsigned v; double x; double y; }; typedef struct _TextureVertex_t TextureVertexT; typedef std::vector< TextureVertexT * > TextureVertexVector; typedef struct _RotateVertex_t RotateVertexT; typedef std::vector< RotateVertexT * > RotateVertexVector; struct _TextureTriangle_t { int vertex[3]; }; typedef struct _TextureTriangle_t TextureTriangleT; typedef std::vector< TextureTriangleT * > TextureTriangleVector; TextureWidget( QWidget * parent = NULL ); virtual ~TextureWidget(); void setModel( Model * model ); void setTexture( int materialId, Texture * texture ); void setInteractive( bool o ); void set3d( bool o ); void setTextureCount( unsigned c ); void setSClamp( bool o ) { m_sClamp = o; setTexture( m_materialId, m_texture ); }; void setTClamp( bool o ) { m_tClamp = o; setTexture( m_materialId, m_texture ); }; void setDrawMode( DrawModeE dm ); void setDrawVertices( bool dv ) { m_drawVertices = dv; }; void setDrawBorder( bool db ) { m_drawBorder = db; }; void setMouseOperation( MouseOperationE op ); void setScaleKeepAspect( bool o ) { m_scaleKeepAspect = o; }; void setScaleFromCenter( bool o ) { m_scaleFromCenter = o; }; void setSolidBackground( bool o ) { m_solidBackground = o; }; void vFlipCoordinates(); void hFlipCoordinates(); void rotateCoordinatesCcw(); void rotateCoordinatesCw(); void setLinesColor( uint32_t newColor ) { m_linesColor = newColor; } void setSelectionColor( uint32_t newColor ) { m_selectionColor = newColor; } int addVertex( double t, double s ); int addTriangle( int v1, int v2, int v3 ); void clearCoordinates(); void getCoordinates( int tri, float * s, float * t ); void saveSelectedUv(); void restoreSelectedUv(); // This is min/max of the current viewport, not of the // vertices in the viewport double getMinViewCoord() { return m_xMin; }; double getMaxViewCoord() { return m_xMax; }; void setRange( double xMin, double yMin, double xMax, double yMax ); void getRange( double & xMin, double & yMin, double & xMax, double & yMax ); // paint my scene on another OpenGL widget void paintOnGlWidget( QOpenGLWidget * w ); public slots: void animationTimeout(); void scrollUp(); void scrollDown(); void scrollLeft(); void scrollRight(); void zoomIn(); void zoomOut(); void scrollTimeout(); void setZoomLevel( double zoom ); signals: void updateCoordinatesSignal(); void updateSelectionDoneSignal(); void updateCoordinatesDoneSignal(); void updateRangeSignal(); void updateRangeDoneSignal(); void updateSeamSignal( double xDiff, double yDiff ); void updateSeamDoneSignal(); void zoomLevelChanged( QString zoomStr ); protected: #if QT_VERSION < QT_VERSION_CHECK( 5, 6, 0 ) qreal devicePixelRatioF() { return devicePixelRatio(); } #endif void mousePressEvent( QMouseEvent * e ) override; void mouseReleaseEvent( QMouseEvent * e ) override; void mouseMoveEvent( QMouseEvent * e ) override; void wheelEvent( QWheelEvent * e ) override; void keyPressEvent( QKeyEvent * e ) override; void moveSelectedVertices( double x, double y ); void updateSelectRegion( double x, double y ); void startScale( double x, double y ); void rotateSelectedVertices( double angle ); void scaleSelectedVertices( double x, double y ); void setViewportDraw(); void setViewportOverlay(); void drawTriangles(); void drawOverlay(); void makeTextureFromImage( const QImage & i, GLuint & t ); void updateGLTexture(); void selectDone(); void drawSelectBox(); void drawRangeBox(); void drawRotationPoint(); void clearSelected(); double getWindowXCoord( int x ); double getWindowYCoord( int y ); void updateCursorShape( int x, int y ); void getDragDirections( double x, double y, bool & all, bool & top, bool & bottom, bool & left, bool & right ); void setDragCursor( bool all, bool top, bool bottom, bool left, bool right ); void initializeGL() override; void paintGL() override; void resizeGL( int w, int h ) override; void paintInternal(); void updateViewport(); void freeRotateVertices(); double distance( const double &, const double &, const double &, const double & ); double max( const double &, const double & ); double adjustToNearest( double angle ); void useLinesColor(); void useSelectionColor(); int m_viewportWidth; int m_viewportHeight; bool m_sClamp; bool m_tClamp; double m_zoom; double m_xCenter; double m_yCenter; int m_lastXPos; int m_lastYPos; Model * m_model; int m_materialId; Texture * m_texture; GLuint m_glTexture; QTimer * m_scrollTimer; ScrollButtonE m_overlayButton; GLuint m_scrollTextures[2]; TextureVertexVector m_vertices; TextureTriangleVector m_triangles; RotateVertexVector m_rotateVertices; DrawModeE m_drawMode; bool m_drawVertices; bool m_drawBorder; bool m_solidBackground; MouseOperationE m_operation; bool m_scaleKeepAspect; bool m_scaleFromCenter; bool m_selecting; bool m_drawBounding; bool m_drawRange; bool m_interactive; bool m_3d; int m_button; // For 3d view QTimer * m_animTimer; double m_xMin; double m_xMax; double m_yMin; double m_yMax; // For move and scale bool m_allowX; bool m_allowY; // For select double m_xSel1; double m_ySel1; double m_xSel2; double m_ySel2; // For rotation double m_xRotPoint; double m_yRotPoint; double m_xRotStart; double m_yRotStart; double m_startAngle; // For projection range double m_xRangeMin; double m_yRangeMin; double m_xRangeMax; double m_yRangeMax; // For projection move/resize bool m_dragAll; bool m_dragTop; bool m_dragBottom; bool m_dragLeft; bool m_dragRight; // For scale typedef struct _ScaleVertices_t { unsigned index; double x; double y; } ScaleVerticesT; typedef std::list ScaleVerticesList; double m_farX; double m_farY; double m_centerX; double m_centerY; double m_startLengthX; double m_startLengthY; uint32_t m_linesColor; uint32_t m_selectionColor; ScaleVerticesList m_scaleList; }; #endif // __TEXWIDGET_H mm3d-1.3.15/src/implui/000077500000000000000000000000001466047437300145455ustar00rootroot00000000000000mm3d-1.3.15/src/implui/Makefile.am000066400000000000000000000072151466047437300166060ustar00rootroot00000000000000 noinst_LIBRARIES = libimplui.a libimplui_HFILES = \ aboutwin.h \ alignwin.h \ animconvertwin.h \ animeditwin.h \ animexportwin.h \ animsetwin.h \ animwidget.h \ animwin.h \ autoassignjointwin.h \ backgroundselect.h \ backgroundwin.h \ boolpanel.h \ boolwin.h \ cal3dprompt.h \ contextgroup.h \ contextinfluences.h \ contextname.h \ contextpanel.h \ contextposition.h \ contextprojection.h \ contextrotation.h \ extrudewin.h \ groupclean.h \ groupwin.h \ helpwin.h \ iqeprompt.h \ jointwin.h \ keycfg.h \ keyvaluewin.h \ licensewin.h \ mapdirection.h \ md3prompt.h \ mergewin.h \ metawin.h \ ms3dprompt.h \ msgqt.h \ mview.h \ newanim.h \ objprompt.h \ offsetwin.h \ painttexturewin.h \ paintwidget.h \ pluginwin.h \ pointwin.h \ projectionwin.h \ qtmain.h \ qttex.h \ smdprompt.h \ spherifywin.h \ statusbar.h \ texturecoord.h \ texwin.h \ transformwin.h \ transimp.h \ valuewin.h \ viewpanel.h \ viewportsettings.h \ viewwin.h libimplui_MOC = \ aboutwin.moc.cc \ alignwin.moc.cc \ animconvertwin.moc.cc \ animeditwin.moc.cc \ animexportwin.moc.cc \ animsetwin.moc.cc \ animwidget.moc.cc \ animwin.moc.cc \ autoassignjointwin.moc.cc \ backgroundselect.moc.cc \ backgroundwin.moc.cc \ boolpanel.moc.cc \ boolwin.moc.cc \ cal3dprompt.moc.cc \ contextgroup.moc.cc \ contextinfluences.moc.cc \ contextname.moc.cc \ contextpanel.moc.cc \ contextposition.moc.cc \ contextprojection.moc.cc \ contextrotation.moc.cc \ extrudewin.moc.cc \ groupclean.moc.cc \ groupwin.moc.cc \ helpwin.moc.cc \ iqeprompt.moc.cc \ jointwin.moc.cc \ pointwin.moc.cc \ painttexturewin.moc.cc \ projectionwin.moc.cc \ keyvaluewin.moc.cc \ licensewin.moc.cc \ md3prompt.moc.cc \ mergewin.moc.cc \ metawin.moc.cc \ ms3dprompt.moc.cc \ mview.moc.cc \ newanim.moc.cc \ objprompt.moc.cc \ offsetwin.moc.cc \ pluginwin.moc.cc \ smdprompt.moc.cc \ spherifywin.moc.cc \ statusbar.moc.cc \ texturecoord.moc.cc \ texwin.moc.cc \ transformwin.moc.cc \ valuewin.moc.cc \ viewportsettings.moc.cc \ viewpanel.moc.cc \ viewwin.moc.cc BUILT_SOURCES = $(libimplui_MOC) libimplui_a_SOURCES = \ aboutwin.cc \ alignwin.cc \ animconvertwin.cc \ animeditwin.cc \ animexportwin.cc \ animsetwin.cc \ animwidget.cc \ animwin.cc \ autoassignjointwin.cc \ backgroundselect.cc \ backgroundwin.cc \ boolpanel.cc \ boolwin.cc \ cal3dprompt.cc \ contextgroup.cc \ contextinfluences.cc \ contextname.cc \ contextpanel.cc \ contextposition.cc \ contextprojection.cc \ contextrotation.cc \ extrudewin.cc \ groupclean.cc \ groupwin.cc \ helpwin.cc \ iqeprompt.cc \ jointwin.cc \ keycfg.cc \ keyvaluewin.cc \ licensewin.cc \ mapdirection.cc \ md3prompt.cc \ mergewin.cc \ metawin.cc \ ms3dprompt.cc \ msgqt.cc \ mview.cc \ newanim.cc \ objprompt.cc \ offsetwin.cc \ painttexturewin.cc \ paintwidget.cc \ pluginwin.cc \ pointwin.cc \ projectionwin.cc \ qtmain.cc \ qttex.cc \ smdprompt.cc \ spherifywin.cc \ statusbar.cc \ texturecoord.cc \ texwin.cc \ transformwin.cc \ transimp.cc \ valuewin.cc \ viewpanel.cc \ viewportsettings.cc \ viewwin.cc \ viewwin_influences.cc \ $(libimplui_MOC) \ $(libimplui_HFILES) %.moc.cc: %.h $(QT_MOC) -o $@ $< %.base.h: %.ui $(QT_UIC) -o $@ $(srcdir)/$*.ui %.base.cc: $(mm3d_UIHEADERS) $(srcdir)/%.ui $(QT_UIC) -o $@ -impl $(builddir)/$*.base.h $(srcdir)/$*.ui # -DQT_NO_CAST_ASCII AM_CPPFLAGS = $(PROFILE) $(COVFLAGS) -Wall -I$(srcdir)/../libmm3d -I$(srcdir)/../mm3dcore -I$(srcdir)/../depui -I$(builddir)/../qtui -I$(srcdir)/../ -DMM3D_EDIT $(all_includes) $(QT_CXXFLAGS) $(LUALIB_CCFLAGS) $(GL_CFLAGS) CLEANFILES = $(libimplui_MOC) $(mm3d_UIHEADERS) *.gcno *.gcda mm3d-1.3.15/src/implui/aboutwin.cc000066400000000000000000000031071466047437300167050ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "aboutwin.h" #include "version.h" #include #define ABOUT_TEXT "Maverick Model 3D - About " \ "

" \ "

Maverick Model 3D

" \ "

" VERSION_STRING "


" \ "https://clover.moe/mm3d

" \ "Copyright © 2004-2008, Kevin Worcester
" \ "Copyright © 2009-2024 Zack Middleton

" \ "
" AboutWin::AboutWin( QWidget * parent ) : QDialog( parent ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( false ); setWindowTitle( tr( "Maverick Model 3D - About") ); resize( 350, 350 ); m_text->setHtml( QString( ABOUT_TEXT ) ); } AboutWin::~AboutWin() { } mm3d-1.3.15/src/implui/aboutwin.h000066400000000000000000000022031466047437300165430ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ABOUTWIN_H #define __ABOUTWIN_H #include "textwin.base.h" #include class AboutWin : public QDialog, public Ui::TextWinBase { Q_OBJECT public: AboutWin( QWidget * parent = NULL ); virtual ~AboutWin(); protected: }; #endif // __ABOUTWIN_H mm3d-1.3.15/src/implui/alignwin.cc000066400000000000000000000070201466047437300166630ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "alignwin.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include using std::list; using std::map; AlignWin::AlignWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ), m_atX( AT_Center ), m_atY( AT_Center ), m_atZ( AT_Center ) { setAttribute( Qt::WA_DeleteOnClose ); setModal( true ); setupUi( this ); m_xCenter->setChecked( true ); m_yCenter->setChecked( true ); m_zCenter->setChecked( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } AlignWin::~AlignWin() { } void AlignWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_alignwin.html", true ); win->show(); } void AlignWin::alignX() { double val = m_xValue->text().toDouble(); log_debug( "aligning x on %f\n", val ); alignSelectedX( m_model, m_atX, val ); DecalManager::getInstance()->modelUpdated( m_model ); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Align X").toUtf8().data() ); } void AlignWin::alignY() { double val = m_yValue->text().toDouble(); log_debug( "aligning y on %f\n", val ); alignSelectedY( m_model, m_atY, val ); DecalManager::getInstance()->modelUpdated( m_model ); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Align Y").toUtf8().data() ); } void AlignWin::alignZ() { double val = m_zValue->text().toDouble(); log_debug( "aligning z on %f\n", val ); alignSelectedZ( m_model, m_atZ, val ); DecalManager::getInstance()->modelUpdated( m_model ); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Align Z").toUtf8().data() ); } void AlignWin::selectedXCenter() { m_atX = AT_Center; } void AlignWin::selectedXMin() { m_atX = AT_Min; } void AlignWin::selectedXMax() { m_atX = AT_Max; } void AlignWin::selectedYCenter() { m_atY = AT_Center; } void AlignWin::selectedYMin() { m_atY = AT_Min; } void AlignWin::selectedYMax() { m_atY = AT_Max; } void AlignWin::selectedZCenter() { m_atZ = AT_Center; } void AlignWin::selectedZMin() { m_atZ = AT_Min; } void AlignWin::selectedZMax() { m_atZ = AT_Max; } void AlignWin::accept() { log_debug( "Alignment complete\n" ); m_model->operationComplete( tr( "Align Selected", "operation complete" ).toUtf8() ); QDialog::accept(); } void AlignWin::reject() { log_debug( "Alignment canceled\n" ); m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/alignwin.h000066400000000000000000000034461466047437300165350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ALIGNWIN_H #define __ALIGNWIN_H #include "alignwin.base.h" #include "align.h" #include class Model; class AlignWin : public QDialog, public Ui::AlignWinBase { Q_OBJECT public: AlignWin( Model *, QWidget * parent = NULL ); virtual ~AlignWin(); public slots: void alignX(); void alignY(); void alignZ(); void selectedXCenter(); void selectedXMin(); void selectedXMax(); void selectedYCenter(); void selectedYMin(); void selectedYMax(); void selectedZCenter(); void selectedZMin(); void selectedZMax(); void accept(); void reject(); void helpNowEvent(); protected: Model * m_model; double m_xMin; double m_xMax; double m_yMin; double m_yMax; double m_zMin; double m_zMax; AlignTypeE m_atX; AlignTypeE m_atY; AlignTypeE m_atZ; }; #endif // __ALIGNWIN_H mm3d-1.3.15/src/implui/animconvertwin.cc000066400000000000000000000141701466047437300201220ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "animconvertwin.h" #include "helpwin.h" #include #include #include #include #include #define COLUMN_ORIGINALNAME 0 #define COLUMN_NAME 1 #define COLUMN_FRAMECOUNT 2 AnimConvertFrameCountDelegate::AnimConvertFrameCountDelegate( QObject *parent ) : QStyledItemDelegate( parent ) { } QWidget *AnimConvertFrameCountDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */ ) const { QSpinBox *editor = new QSpinBox( parent ); editor->setFrame( false ); editor->setMinimum( 0 ); editor->setMaximum( 9999 ); return editor; } void AnimConvertFrameCountDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const { int value = index.model()->data( index, Qt::EditRole ).toInt(); QSpinBox *spinBox = static_cast( editor ); spinBox->setValue( value ); } void AnimConvertFrameCountDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const { QSpinBox *spinBox = static_cast( editor ); spinBox->interpretText(); int value = spinBox->value(); model->setData( index, value, Qt::EditRole ); } void AnimConvertFrameCountDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */ ) const { editor->setGeometry( option.rect ); } AnimConvertWindow::AnimConvertWindow( QWidget * parent ) : QDialog( parent ), m_frameCountDelegate( NULL ), m_model( NULL ), m_mode( Model::AnimationModeE::ANIMMODE_NONE ), m_animIndicies() { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); m_frameCountDelegate = new AnimConvertFrameCountDelegate(); m_animTable->setItemDelegateForColumn( COLUMN_FRAMECOUNT, m_frameCountDelegate ); m_animTable->horizontalHeader()->resizeSection( COLUMN_ORIGINALNAME, 200 ); m_animTable->horizontalHeader()->resizeSection( COLUMN_NAME, 200 ); m_animTable->horizontalHeader()->resizeSection( COLUMN_FRAMECOUNT, 30 ); m_animTable->setSelectionMode( QAbstractItemView::SelectionMode::NoSelection ); m_animTable->setEditTriggers( QAbstractItemView::CurrentChanged ); } AnimConvertWindow::~AnimConvertWindow() { } void AnimConvertWindow::setAnimationData( Model * model, Model::AnimationModeE mode, const std::list & animIndicies ) { m_model = model; m_mode = mode; m_animIndicies = animIndicies; switch ( m_mode ) { case Model::ANIMMODE_SKELETAL: m_convertLabel->setText( tr( "Convert Skeletal to Frame:" ) ); m_animTable->horizontalHeaderItem( COLUMN_ORIGINALNAME )->setText( tr( "Skeletal Animation" ) ); break; case Model::ANIMMODE_FRAME: m_convertLabel->setText( tr( "Convert Frame to Frame:" ) ); m_animTable->horizontalHeaderItem( COLUMN_ORIGINALNAME )->setText( tr( "Frame Animation" ) ); break; case Model::ANIMMODE_FRAMERELATIVE: m_convertLabel->setText( tr( "Convert Frame Relative to Frame:" ) ); m_animTable->horizontalHeaderItem( COLUMN_ORIGINALNAME )->setText( tr( "Frame Relative Animation" ) ); break; default: m_convertLabel->setText( tr( "Convert Unknown Type to Frame:" ) ); m_animTable->horizontalHeaderItem( COLUMN_ORIGINALNAME )->setText( tr( "Unknown Type Animation" ) ); break; } m_animTable->setRowCount( m_animIndicies.size() ); unsigned row = 0; std::list::iterator it; for ( it = m_animIndicies.begin(), row = 0; it != m_animIndicies.end(); it++, row++ ) { unsigned t = *it; const char * name = m_model->getAnimName( m_mode, t ); unsigned frameCount = m_model->getAnimFrameCount( m_mode, t ); QTableWidgetItem *originalNameItem = new QTableWidgetItem( QString::fromUtf8( name ) ); originalNameItem->setFlags( originalNameItem->flags() & ~Qt::ItemIsEditable ); m_animTable->setItem( row, COLUMN_ORIGINALNAME, originalNameItem ); QTableWidgetItem *nameItem = new QTableWidgetItem( QString::fromUtf8( name ) ); m_animTable->setItem( row, COLUMN_NAME, nameItem ); QString frameCountText; frameCountText.setNum( frameCount ); QTableWidgetItem *frameCountItem = new QTableWidgetItem( frameCountText ); m_animTable->setItem( row, COLUMN_FRAMECOUNT, frameCountItem ); } } void AnimConvertWindow::convertClicked() { unsigned row = 0; std::list::iterator it; for ( it = m_animIndicies.begin(), row = 0; it != m_animIndicies.end(); it++, row++ ) { unsigned t = *it; QTableWidgetItem *nameItem = m_animTable->item( row, COLUMN_NAME ); QString newName = nameItem->text(); QTableWidgetItem *frameCountItem = m_animTable->item( row, COLUMN_FRAMECOUNT ); unsigned newFrameCount = frameCountItem->text().toInt(); m_model->convertAnimToFrame( m_mode, t, newName.toUtf8().data(), newFrameCount ); } accept(); } void AnimConvertWindow::cancelClicked() { reject(); } void AnimConvertWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_animconvertwin.html", true ); win->show(); } mm3d-1.3.15/src/implui/animconvertwin.h000066400000000000000000000044571466047437300177730ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ANIMCONVERTWIN_H #define __ANIMCONVERTWIN_H #include "animconvertwin.base.h" #include "model.h" #include #include class AnimConvertFrameCountDelegate : public QStyledItemDelegate { Q_OBJECT public: AnimConvertFrameCountDelegate( QObject *parent = NULL ); QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const override; void setEditorData( QWidget *editor, const QModelIndex &index ) const override; void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const override; void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const override; }; class AnimConvertWindow : public QDialog, public Ui::AnimConvertWinBase { Q_OBJECT public: AnimConvertWindow( QWidget * parent = NULL ); virtual ~AnimConvertWindow(); void setAnimationData( Model * model, Model::AnimationModeE mode, const std::list & animIndicies ); public slots: void helpNowEvent(); void convertClicked(); void cancelClicked(); protected: AnimConvertFrameCountDelegate *m_frameCountDelegate; Model *m_model; Model::AnimationModeE m_mode; std::list m_animIndicies; }; #endif // __ANIMCONVERTWIN_H mm3d-1.3.15/src/implui/animeditwin.cc000066400000000000000000000144421466047437300173710ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "animeditwin.h" #include "helpwin.h" #include #include #include #include #include #include #define COLUMN_NAME 0 #define COLUMN_FRAMECOUNT 1 #define COLUMN_FPS 2 #define COLUMN_LOOPING 3 AnimEditFrameCountDelegate::AnimEditFrameCountDelegate( QObject *parent ) : QStyledItemDelegate( parent ) { } QWidget *AnimEditFrameCountDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */ ) const { QSpinBox *editor = new QSpinBox( parent ); editor->setFrame( false ); editor->setMinimum( 0 ); editor->setMaximum( 9999 ); return editor; } void AnimEditFrameCountDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const { int value = index.model()->data( index, Qt::EditRole ).toInt(); QSpinBox *spinBox = static_cast( editor ); spinBox->setValue( value ); } void AnimEditFrameCountDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const { QSpinBox *spinBox = static_cast( editor ); spinBox->interpretText(); int value = spinBox->value(); model->setData( index, value, Qt::EditRole ); } void AnimEditFrameCountDelegate::updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */ ) const { editor->setGeometry( option.rect ); } AnimEditWindow::AnimEditWindow( QWidget * parent ) : QDialog( parent ), m_frameCountDelegate( NULL ), m_model( NULL ), m_mode( Model::AnimationModeE::ANIMMODE_NONE ), m_animIndicies() { setupUi( this ); setModal( true ); m_frameCountDelegate = new AnimEditFrameCountDelegate(); m_animTable->setItemDelegateForColumn( COLUMN_FRAMECOUNT, m_frameCountDelegate ); m_animTable->horizontalHeader()->resizeSection( COLUMN_NAME, 300 ); m_animTable->horizontalHeader()->resizeSection( COLUMN_FRAMECOUNT, 100 ); m_animTable->horizontalHeader()->resizeSection( COLUMN_FPS, 100 ); m_animTable->horizontalHeader()->resizeSection( COLUMN_LOOPING, 50 ); m_animTable->setSelectionMode( QAbstractItemView::SelectionMode::NoSelection ); m_animTable->setEditTriggers( QAbstractItemView::CurrentChanged ); } AnimEditWindow::~AnimEditWindow() { } void AnimEditWindow::setAnimationData( Model * model, Model::AnimationModeE mode, const std::list & animIndicies ) { m_model = model; m_mode = mode; m_animIndicies = animIndicies; switch ( m_mode ) { case Model::ANIMMODE_SKELETAL: m_convertLabel->setText( tr( "Editing Skeletal animations:" ) ); break; case Model::ANIMMODE_FRAME: m_convertLabel->setText( tr( "Editing Frame animations:" ) ); break; case Model::ANIMMODE_FRAMERELATIVE: m_convertLabel->setText( tr( "Editing Frame Relative animations:" ) ); break; default: m_convertLabel->setText( tr( "Editing Unknown Type animations:" ) ); break; } m_animTable->setRowCount( m_animIndicies.size() ); unsigned row = 0; std::list::iterator it; for ( it = m_animIndicies.begin(), row = 0; it != m_animIndicies.end(); it++, row++ ) { unsigned t = *it; const char * name = m_model->getAnimName( m_mode, t ); unsigned frameCount = m_model->getAnimFrameCount( m_mode, t ); double fps = m_model->getAnimFPS( m_mode, t ); bool loop = m_model->getAnimLooping( m_mode, t ); QTableWidgetItem *nameItem = new QTableWidgetItem( QString::fromUtf8( name ) ); m_animTable->setItem( row, COLUMN_NAME, nameItem ); QString frameCountText; frameCountText.setNum( frameCount ); QTableWidgetItem *frameCountItem = new QTableWidgetItem( frameCountText ); m_animTable->setItem( row, COLUMN_FRAMECOUNT, frameCountItem ); QString fpsText; fpsText.setNum( fps ); QTableWidgetItem *fpsItem = new QTableWidgetItem( fpsText ); m_animTable->setItem( row, COLUMN_FPS, fpsItem ); QString loopingText; loopingText.setNum( loop ); QCheckBox *loopingItem = new QCheckBox(); loopingItem->setChecked( loop ); m_animTable->setCellWidget( row, COLUMN_LOOPING, loopingItem ); } } void AnimEditWindow::okClicked() { unsigned row = 0; std::list::iterator it; for ( it = m_animIndicies.begin(), row = 0; it != m_animIndicies.end(); it++, row++ ) { unsigned t = *it; QTableWidgetItem *nameItem = m_animTable->item( row, COLUMN_NAME ); QString newName = nameItem->text(); QTableWidgetItem *frameCountItem = m_animTable->item( row, COLUMN_FRAMECOUNT ); unsigned newFrameCount = frameCountItem->text().toInt(); QTableWidgetItem *fpsItem = m_animTable->item( row, COLUMN_FPS ); double newFPS = fpsItem->text().toDouble(); QCheckBox *loopingItem = static_cast( m_animTable->cellWidget( row, COLUMN_LOOPING ) ); bool newLoop = loopingItem->isChecked(); m_model->setAnimName( m_mode, t, newName.toUtf8().data() ); m_model->setAnimFrameCount( m_mode, t, newFrameCount ); m_model->setAnimFPS( m_mode, t, newFPS ); m_model->setAnimLooping( m_mode, t, newLoop ); } // This isn't complete until Animation Sets exits. //m_model->operationComplete( tr( "Edit Animations", "operation complete" ).toUtf8() ); accept(); } void AnimEditWindow::cancelClicked() { reject(); } mm3d-1.3.15/src/implui/animeditwin.h000066400000000000000000000043601466047437300172310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ANIMEDITWIN_H #define __ANIMEDITWIN_H #include "animeditwin.base.h" #include "model.h" #include #include class AnimEditFrameCountDelegate : public QStyledItemDelegate { Q_OBJECT public: AnimEditFrameCountDelegate( QObject *parent = NULL ); QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const override; void setEditorData( QWidget *editor, const QModelIndex &index ) const override; void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const override; void updateEditorGeometry( QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const override; }; class AnimEditWindow : public QDialog, public Ui::AnimEditWinBase { Q_OBJECT public: AnimEditWindow( QWidget * parent = NULL ); virtual ~AnimEditWindow(); void setAnimationData( Model * model, Model::AnimationModeE mode, const std::list & animIndicies ); public slots: void okClicked(); void cancelClicked(); protected: AnimEditFrameCountDelegate *m_frameCountDelegate; Model *m_model; Model::AnimationModeE m_mode; std::list m_animIndicies; }; #endif // __ANIMCONVERTWIN_H mm3d-1.3.15/src/implui/animexportwin.cc000066400000000000000000000217561466047437300177730ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "animexportwin.h" #include "helpwin.h" #include "viewpanel.h" #include "model.h" #include "mview.h" #include "3dmprefs.h" #include "misc.h" #include "texture.h" #include "texmgr.h" #include "msg.h" #include "mm3dport.h" #include #include #include #include #include #include #include #include #include #include #include AnimExportWindow::AnimExportWindow( Model * model, ViewPanel * viewPanel, QWidget * parent ) : QDialog( parent ), m_model( model ), m_viewPanel( viewPanel ) { setModal( true ); setupUi( this ); QString path = QString::fromUtf8( g_prefs( "ui_animexport_dir" ).stringValue().c_str() ); if ( g_prefs.exists( "ui_animexport_format" ) ) { int f = g_prefs( "ui_animexport_format" ).intValue(); if ( f >= 0 && f < m_formatValue->count() ) { m_formatValue->setCurrentIndex( f ); } } if ( g_prefs.exists( "ui_animexport_framerate" ) ) { double fps = g_prefs( "ui_animexport_framerate" ).doubleValue(); if ( fps > 0.0001 ) { char fpsStr[20] = ""; PORT_snprintf( fpsStr, sizeof(fpsStr), "%f", fps ); m_frameRateValue->setText( QString(fpsStr) ); } } if ( g_prefs.exists( "ui_animexport_seconds" ) ) { int sec = g_prefs( "ui_animexport_seconds" ).intValue(); if ( sec > 0 ) { char secStr[20] = ""; PORT_snprintf( secStr, sizeof(secStr), "%d", sec ); m_secondsValue->setText( QString(secStr) ); } } if ( g_prefs.exists( "ui_animexport_iterations" ) ) { int iterations = g_prefs( "ui_animexport_iterations" ).intValue(); if ( iterations > 0 ) { char itStr[20] = ""; PORT_snprintf( itStr, sizeof(itStr), "%d", iterations ); m_iterationsValue->setText( QString(itStr) ); } } if ( m_model ) { bool labelAnims = false; unsigned scount = m_model->getAnimCount( Model::ANIMMODE_SKELETAL ); unsigned fcount = m_model->getAnimCount( Model::ANIMMODE_FRAME ); if ( scount > 0 && fcount > 0 ) { labelAnims = true; } unsigned a = 0; for ( a = 0; a < scount; a++ ) { QString name = labelAnims ? tr( "Skeletal - ", "Skeletal Animation prefix" ) : QString(""); name += m_model->getAnimName( Model::ANIMMODE_SKELETAL, a ); m_animValue->insertItem( a, name ); } for ( a = 0; a < fcount; a++ ) { QString name = labelAnims ? tr( "Frame - ", "Frame Animation prefix" ) : QString(""); name += m_model->getAnimName( Model::ANIMMODE_FRAME, a ); m_animValue->insertItem( scount + a, name ); } const char * filename = m_model->getFilename(); if ( path.isNull() || path.isEmpty() ) { std::string fullName = ""; std::string fullPath = ""; std::string baseName = ""; normalizePath( filename, fullName, fullPath, baseName ); if ( is_directory( fullPath.c_str() ) ) { path = fullPath.c_str(); } } } if ( path.isNull() || path.isEmpty() ) { char * cwd = PORT_get_current_dir_name(); if ( cwd ) { path = cwd; free( cwd ); } } m_directoryLabel->setText( path ); if ( m_viewPanel ) { int index = -1; unsigned count = m_viewPanel->getModelViewCount(); for ( unsigned m = 0; m < count; m++ ) { ModelView * v = m_viewPanel->getModelView( m ); QString name = tr( "[None]", "No viewport for animation image export" ); if ( v ) { name = tr("Viewport %1 - ").arg(m+1); name += v->getViewDirectionLabel(); if ( index == -1 && v->getViewDirection() == 0 ) { index = m; } } m_viewportValue->insertItem( m, name ); } if ( index >= 0 ) { m_viewportValue->setCurrentIndex( index ); } } QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } AnimExportWindow::~AnimExportWindow() { } void AnimExportWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_animexportwin.html", true ); win->show(); } void AnimExportWindow::accept() { if ( m_viewPanel == NULL || m_model == NULL ) { return; } ModelView * v = m_viewPanel->getModelView( m_viewportValue->currentIndex() ); if ( v == NULL ) { return; } if ( !v->m_modelView->isValid() ) { // invalid opengl context return; } Model::AnimationModeE mode = Model::ANIMMODE_SKELETAL; unsigned a = m_animValue->currentIndex(); if ( a >= m_model->getAnimCount( mode ) ) { a -= m_model->getAnimCount( mode ); mode = Model::ANIMMODE_FRAME; } double fps = m_model->getAnimFPS( mode, a ); double spf = ( 1.0 / fps ); double duration = 0.0; double tm = 0.0; double outfps = m_frameRateValue->text().toDouble(); if ( outfps < 0.0001 ) { msg_warning( (const char *) tr("Must have more than 0 frames per second").toUtf8() ); return; } double interval = (1.0 / outfps); if ( m_timeButton->isChecked() ) { duration = m_secondsValue->text().toDouble(); } else { duration = spf * m_iterationsValue->text().toInt() * m_model->getAnimFrameCount( mode, a ); } if ( duration <= 0.0 ) { msg_warning( (const char *) tr("Must have more than 0 seconds of animation").toUtf8() ); return; } QString path = m_directoryLabel->text(); if ( is_directory( path.toUtf8() ) ) { g_prefs( "ui_animexport_dir" ) = (const char *) path.toUtf8(); g_prefs( "ui_animexport_format" ) = m_formatValue->currentIndex(); g_prefs( "ui_animexport_framerate" ) = m_frameRateValue->text().toDouble(); g_prefs( "ui_animexport_seconds" ) = m_secondsValue->text().toInt(); g_prefs( "ui_animexport_iterations" ) = m_iterationsValue->text().toInt(); bool enable = m_model->setUndoEnabled( false ); m_model->setCurrentAnimation( mode, a ); int frameNum = 0; char formatStr[20] = ""; QString saveFormat = QString( "JPEG" ); switch ( m_formatValue->currentIndex() ) { case 0: strcpy( formatStr, "%s/anim_%04d.jpg" ); break; case 1: strcpy( formatStr, "%s/anim_%d.jpg" ); break; case 2: strcpy( formatStr, "%s/anim_%04d.png" ); saveFormat = QString( "PNG" ); break; case 3: default: strcpy( formatStr, "%s/anim_%d.png" ); saveFormat = QString( "PNG" ); break; } this->hide(); bool prompt = true; bool keepGoing = true; while ( keepGoing && tm <= duration ) { m_model->setCurrentAnimationTime( tm ); v->updateCaptureGL(); frameNum++; QString file = QString::asprintf( formatStr, (const char *) path.toUtf8(), frameNum ); QImage img = v->grabFramebuffer(); if ( !img.save( file, saveFormat.toUtf8(), 100 ) && prompt ) { QString msg = tr( "Could not write file: " ) + QString("\n"); msg += file; msg_error( (const char *) msg.toUtf8() ); keepGoing = false; } tm += interval; } m_model->setNoAnimation(); m_model->setUndoEnabled( enable ); v->updateView(); QDialog::accept(); } else { msg_warning( (const char *) tr("Output directory does not exist.").toUtf8() ); } } void AnimExportWindow::reject() { QDialog::reject(); } void AnimExportWindow::directoryButtonClicked() { QString dir = QFileDialog::getExistingDirectory( this, "Select an output directory", m_directoryLabel->text() ); if ( ! dir.isNull() && ! dir.isEmpty() ) { m_directoryLabel->setText( dir ); } } mm3d-1.3.15/src/implui/animexportwin.h000066400000000000000000000027021466047437300176230ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ANIMEXPORTWIN_H #define __ANIMEXPORTWIN_H #include "animexportwin.base.h" #include "model.h" #include class ViewPanel; class Model; class AnimExportWindow : public QDialog, public Ui::AnimExportWinBase { Q_OBJECT public: AnimExportWindow( Model * model, ViewPanel * viewPanel, QWidget * parent = NULL ); virtual ~AnimExportWindow(); public slots: void helpNowEvent(); void accept(); void reject(); void directoryButtonClicked(); protected: Model * m_model; ViewPanel * m_viewPanel; }; #endif // __ANIMEXPORTWIN_H mm3d-1.3.15/src/implui/animsetwin.cc000066400000000000000000000410451466047437300172360ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "animsetwin.h" #include "animconvertwin.h" #include "animeditwin.h" #include "decalmgr.h" #include "log.h" #include "msg.h" #include "3dmprefs.h" #include "helpwin.h" #include "newanim.h" #include #include #include #include #include #include using std::list; AnimSetWindow::AnimSetWindow( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setupUi( this ); setModal( true ); m_animType->insertItem( 0, tr( "Skeletal Animation" ) ); m_animType->insertItem( 1, tr( "Frame Animation" ) ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); unsigned skelCount = m_model->getAnimCount( Model::ANIMMODE_SKELETAL ); unsigned frameCount = m_model->getAnimCount( Model::ANIMMODE_FRAME ); unsigned relativeCount = m_model->getAnimCount( Model::ANIMMODE_FRAMERELATIVE ); if ( skelCount > 0 ) { m_animType->setCurrentIndex(0); } else if ( frameCount > 0 ) { m_animType->setCurrentIndex(1); } else if ( relativeCount > 0 ) { m_animType->setCurrentIndex(2); } else { m_animType->setCurrentIndex(0); } animModeSelected( m_animType->currentIndex() ); //Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); } AnimSetWindow::~AnimSetWindow() { } void AnimSetWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_animsetwin.html", true ); win->show(); } void AnimSetWindow::animModeSelected( int index ) { switch( index ) { case 0: m_convertButton->setEnabled( true ); m_mergeButton->setEnabled( true ); break; case 1: m_convertButton->setEnabled( false ); m_mergeButton->setEnabled( false ); break; case 2: m_convertButton->setEnabled( true ); m_mergeButton->setEnabled( false ); break; default: break; } fillAnimationList(); } void AnimSetWindow::upClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); unsigned t = 0; for ( t = 0; t < count && m_animList->item(t)->isSelected(); t++ ) { // do nothing (loop finds first unselected item) } unsigned lastUnselected = t; bool moving = false; // t is initialized above for ( ; t < count; t++ ) { if ( moving ) { if ( ! m_animList->item(t)->isSelected() ) { unsigned oldIndex = lastUnselected; unsigned newIndex = t - 1; m_model->moveAnimation( mode, oldIndex, newIndex ); moving = false; QListWidgetItem * item = m_animList->takeItem( oldIndex ); m_animList->insertItem( newIndex, item ); log_debug( "moved item from %d to %d\n", oldIndex, newIndex ); lastUnselected = newIndex; t = newIndex; } } else { if ( m_animList->item(t)->isSelected() ) { moving = true; } else { lastUnselected = t; } } } if ( moving ) { unsigned newIndex = count - 1; m_model->moveAnimation( mode, lastUnselected, newIndex ); QListWidgetItem * item = m_animList->takeItem( lastUnselected ); m_animList->insertItem( newIndex, item ); log_debug( "moved item from %d to %d\n", lastUnselected, newIndex ); } } void AnimSetWindow::downClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); int t = 0; for ( t = count - 1; t >= 0 && m_animList->item(t)->isSelected(); t-- ) { // do nothing (loop finds first unselected item) } unsigned lastUnselected = t; bool moving = false; // t is initialized above for ( ; t >= 0; t-- ) { if ( moving ) { if ( ! m_animList->item(t)->isSelected() ) { unsigned oldIndex = lastUnselected; unsigned newIndex = t + 1; m_model->moveAnimation( mode, oldIndex, newIndex ); moving = false; QListWidgetItem * item = m_animList->takeItem( oldIndex ); m_animList->insertItem( newIndex, item ); log_debug( "moved item from %d to %d\n", oldIndex, newIndex ); lastUnselected = newIndex; t = newIndex; } } else { if ( m_animList->item(t)->isSelected() ) { moving = true; } else { lastUnselected = t; } } } if ( moving ) { unsigned newIndex = 0; m_model->moveAnimation( mode, lastUnselected, newIndex ); QListWidgetItem * item = m_animList->takeItem( lastUnselected ); m_animList->insertItem( newIndex, item ); log_debug( "moved item from %d to %d\n", lastUnselected, newIndex ); } } void AnimSetWindow::newClicked() { NewAnim win( this ); g_prefs.setDefault( "ui_new_anim_type", 0 ); win.setSkeletal( g_prefs( "ui_new_anim_type" ).intValue() == 0 ); if ( win.exec() ) { g_prefs( "ui_new_anim_type" ) = win.isSkeletal() ? 0 : 1; QString name = win.getAnimName(); Model::AnimationModeE mode = win.isSkeletal() ? Model::ANIMMODE_SKELETAL : Model::ANIMMODE_FRAME; unsigned frameCount = win.getAnimFrameCount(); double fps = win.getAnimFPS(); bool loop = win.getAnimLooping(); int anim = m_model->addAnimation( mode, name.toUtf8() ); m_model->setAnimFrameCount( mode, anim, frameCount ); m_model->setAnimFPS( mode, anim, fps ); m_model->setAnimLooping( mode, anim, loop ); m_model->setCurrentAnimation( mode, anim ); if ( mode == Model::ANIMMODE_SKELETAL ) { m_animType->setCurrentIndex(0); } else if ( mode == Model::ANIMMODE_FRAME ) { m_animType->setCurrentIndex(1); } else if ( mode == Model::ANIMMODE_FRAMERELATIVE ) { m_animType->setCurrentIndex(2); } else { m_animType->setCurrentIndex(0); } animModeSelected( m_animType->currentIndex() ); int num = m_animList->count() - 1; if ( num >= 0 ) { m_animList->setCurrentItem(m_animList->item(num)); m_animList->clearSelection(); m_animList->item(num)->setSelected( true ); } } } void AnimSetWindow::editClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); std::list animIndicies; for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item(t)->isSelected() ) { animIndicies.push_back( t ); } } AnimEditWindow aew( this ); aew.setAnimationData( m_model, mode, animIndicies ); aew.exec(); if ( !animIndicies.empty() ) { fillAnimationList(); list::iterator it; m_animList->setCurrentItem(m_animList->item(animIndicies.back())); count = m_animList->count(); for ( unsigned int t = 0; t < count; ++t ) { m_animList->item(t)->setSelected( false ); } for ( it = animIndicies.begin(); it != animIndicies.end(); it++ ) { m_animList->item( *it )->setSelected( true ); } } } void AnimSetWindow::deleteClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); unsigned lastDeleted = 0; bool refillList = false; for ( int t = count - 1; t >= 0; t-- ) { if ( m_animList->item(t)->isSelected() ) { m_model->deleteAnimation( mode, t ); refillList = true; lastDeleted = t; } } if ( refillList ) { fillAnimationList(); count = m_animList->count(); for ( unsigned int t = 0; t < count; ++t ) { m_animList->item(t)->setSelected( false ); } if ( count > 0 ) { if ( lastDeleted < count ) { m_animList->setCurrentItem(m_animList->item(lastDeleted)); } else { m_animList->setCurrentItem(m_animList->item(count - 1)); } } } } void AnimSetWindow::copyClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); list newAnims; unsigned count = m_animList->count(); for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item(t)->isSelected() ) { QString name = m_animList->item( t )->text(); name += QString( " " ) + tr( "copy" ); int num = m_model->copyAnimation( mode, t, name.toUtf8() ); if ( num >= 0 ) { newAnims.push_back( num ); } } } if ( !newAnims.empty() ) { fillAnimationList(); list::iterator it; m_animList->setCurrentItem(m_animList->item(newAnims.back())); count = m_animList->count(); for ( unsigned int t = 0; t < count; ++t ) { m_animList->item(t)->setSelected( false ); } for ( it = newAnims.begin(); it != newAnims.end(); it++ ) { m_animList->item( *it )->setSelected( true ); } } } void AnimSetWindow::splitClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); bool refillList = false; int splitNum = 0; for ( int t = count - 1; t >= 0; t-- ) { if ( m_animList->item(t)->isSelected() ) { splitNum = t; if ( m_model->getAnimFrameCount( mode, t ) >= 2 ) { bool ok = false; QString name = QString::fromUtf8( m_model->getAnimName( mode, t ) ); unsigned frame = QInputDialog::getInt( this, tr("Split at frame", "Split animation frame window title" ), tr("Split", "'Split' refers to splitting an animation into two separate animations" ) + QString(" ") + name + QString(" ") + tr("at frame number", "the frame number where the second (split) animation begins" ), 2, 2, m_model->getAnimFrameCount( mode, t ), 1, &ok ); if ( ok ) { name = name + " " + tr("split"); if ( m_model->splitAnimation( mode, t, name.toUtf8(), frame - 1 ) ) { refillList = true; } } } else { QMessageBox::information( this, tr("Cannot Split", "Cannot split animation window title"), tr("Must have at least 2 frames to split", "split animation" ), QMessageBox::Ok ); } } } if ( refillList ) { fillAnimationList(); m_animList->setCurrentItem(m_animList->item(splitNum)); count = m_animList->count(); for ( unsigned int t = 0; t < count; ++t ) { m_animList->item(t)->setSelected( false ); } m_animList->item( splitNum )->setSelected( true ); } } void AnimSetWindow::joinClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); int joinNum = -1; bool joined = false; unsigned currentAnim = 0; // indices change, need this to keep track of real index for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item(t)->isSelected() ) { if ( joinNum >= 0 ) { log_debug( "joining %d to %d\n", t, joinNum ); m_model->joinAnimations( mode, joinNum, currentAnim ); joined = true; } else { log_debug( "animation to join to is %d\n", t ); joinNum = t; currentAnim++; } } else { currentAnim++; } } if ( joined ) { fillAnimationList(); m_animList->setCurrentItem(m_animList->item(joinNum)); count = m_animList->count(); for ( unsigned int t = 0; t < count; ++t ) { m_animList->item(t)->setSelected( false ); } m_animList->item( joinNum )->setSelected( true ); } } void AnimSetWindow::mergeClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); if ( mode == Model::ANIMMODE_SKELETAL ) { unsigned count = m_animList->count(); int mergeNum = -1; bool merged = false; unsigned currentAnim = 0; // indices change, need this to keep track of real index for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item(t)->isSelected() ) { if ( mergeNum >= 0 ) { unsigned fc1 = m_model->getAnimFrameCount( mode, mergeNum ); unsigned fc2 = m_model->getAnimFrameCount( mode, currentAnim ); if ( fc1 == fc2 ) { log_debug( "merging %d to %d\n", t, mergeNum ); m_model->mergeAnimations( mode, mergeNum, currentAnim ); merged = true; } else { QString str; str = tr("Cannot merge animation %1 and %2,\n frame counts differ.") .arg(m_model->getAnimName( mode, mergeNum )) .arg(m_model->getAnimName( mode, currentAnim )); msg_error( (const char *) str.toUtf8() ); } } else { log_debug( "animation to merge to is %d\n", t ); mergeNum = t; currentAnim++; } } else { currentAnim++; } } if ( merged ) { fillAnimationList(); m_animList->setCurrentItem(m_animList->item(mergeNum)); count = m_animList->count(); for ( unsigned int t = 0; t < count; ++t ) { m_animList->item(t)->setSelected( false ); } m_animList->item( mergeNum )->setSelected( true ); } } else { msg_error( (const char *) tr("Can only merge skeletal animations.").toUtf8() ); } } void AnimSetWindow::convertClicked() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); unsigned count = m_animList->count(); std::list animIndicies; for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item(t)->isSelected() ) { animIndicies.push_back( t ); } } AnimConvertWindow acw( this ); acw.setAnimationData( m_model, mode, animIndicies ); acw.exec(); } void AnimSetWindow::accept() { m_model->operationComplete( tr( "Animation changes", "operation complete" ).toUtf8() ); QDialog::accept(); } void AnimSetWindow::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } void AnimSetWindow::fillAnimationList() { Model::AnimationModeE mode = indexToMode( m_animType->currentIndex() ); m_animList->clear(); unsigned count = m_model->getAnimCount( mode ); for ( unsigned t = 0; t < count; t++ ) { m_animList->insertItem( t, QString::fromUtf8( m_model->getAnimName( mode, t ) ) ); } if ( count > 0 ) { m_animList->setCurrentItem(m_animList->item(0)); } } Model::AnimationModeE AnimSetWindow::indexToMode( int index ) { Model::AnimationModeE mode = Model::ANIMMODE_SKELETAL; switch( index ) { case 0: mode = Model::ANIMMODE_SKELETAL; break; case 1: mode = Model::ANIMMODE_FRAME; break; case 2: mode = Model::ANIMMODE_FRAMERELATIVE; break; default: break; } return mode; } mm3d-1.3.15/src/implui/animsetwin.h000066400000000000000000000033001466047437300170700ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ANIMSETWIN_H #define __ANIMSETWIN_H #include "animsetwin.base.h" #include "model.h" #include class AnimSetWindow : public QDialog, public Ui::AnimSetWinBase { Q_OBJECT public: AnimSetWindow( Model * model, QWidget * parent = NULL ); virtual ~AnimSetWindow(); public slots: void helpNowEvent(); void animModeSelected( int index ); void upClicked(); void downClicked(); void newClicked(); void editClicked(); void deleteClicked(); void copyClicked(); void splitClicked(); void joinClicked(); void mergeClicked(); void convertClicked(); void accept(); void reject(); protected: void fillAnimationList(); Model::AnimationModeE indexToMode( int index ); Model * m_model; }; #endif // __ANIMSETWIN_H mm3d-1.3.15/src/implui/animwidget.cc000066400000000000000000000604531466047437300172140ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "animwidget.h" #include "decalmgr.h" #include "log.h" #include "msg.h" #include "newanim.h" #include "3dmprefs.h" #include "helpwin.h" #include #include #include #include #include #include #include #include #include #include #include enum { ANIMWIN_HELP_ID, ANIMWIN_UNDO_ID, ANIMWIN_REDO_ID }; static int getSliderTickInterval( int val ) { if ( val > 25000 ) { return 5000; } if ( val > 10000 ) { return 1000; } if ( val > 2500 ) { return 500; } if ( val > 1000 ) { return 100; } if ( val > 250 ) { return 50; } if ( val > 100 ) { return 10; } if ( val > 25 ) { return 5; } return 1; } AnimWidget::AnimWidget( Model * model, bool isUndo, QWidget * parent ) : QWidget( parent ), m_model( model ), m_playing( false ), m_undoing( isUndo ), m_ignoreChange( false ) { setupUi( this ); // Don't allow text editing in the frame count QSpinBox. It updates // the frame count immediately. 9 -> 10 will truncate to 1 frame and // then add empty frames. QLineEdit *lineEdit = m_frameCount->findChild(); lineEdit->setReadOnly( true ); lineEdit->setFocusPolicy( Qt::NoFocus ); log_debug( "AnimWidget constructor\n" ); m_animTimer = new QTimer( this ); connect( m_animTimer, SIGNAL(timeout()), this, SLOT(timeElapsed()) ); initialize( model, isUndo ); } AnimWidget::~AnimWidget() { } void AnimWidget::initialize( Model * model, bool isUndo ) { m_needShutdown = true; m_model = model; m_animName->clear(); //m_skelNew->setDefault( false ); m_countSlider->setTickPosition( QSlider::TicksBelow ); m_loop->setChecked( m_model->getAnimLooping(m_model->getAnimationMode(), m_model->getCurrentAnimation()) ); m_skelAnimCount = m_model->getAnimCount( Model::ANIMMODE_SKELETAL ); m_frameAnimCount = m_model->getAnimCount( Model::ANIMMODE_FRAME ); m_animCount = m_skelAnimCount + m_frameAnimCount; insertAnimationNames(); if ( isUndo ) { Model::AnimationModeE mode = m_model->getAnimationMode(); if ( !mode ) { if ( m_skelAnimCount > 0 ) { mode = Model::ANIMMODE_SKELETAL; m_model->setCurrentAnimation( mode, (unsigned int) 0 ); } else if ( m_frameAnimCount > 0 ) { mode = Model::ANIMMODE_FRAME; m_model->setCurrentAnimation( mode, (unsigned int) 0 ); } } unsigned anim = m_model->getCurrentAnimation(); unsigned frame = m_model->getCurrentAnimationFrame(); m_model->setCurrentAnimation( mode, anim ); m_model->setCurrentAnimationFrame( frame ); m_animName->setCurrentIndex( animToIndex( mode, anim ) ); m_undoing = false; m_currentAnim = anim; m_currentFrame = frame; m_mode = mode; refreshPage(); } else { m_currentAnim = 0; m_currentFrame = 0; m_mode = Model::ANIMMODE_SKELETAL; if ( m_skelAnimCount > 0 ) { m_currentAnim = indexToAnim( m_animName->currentIndex() ); m_model->setCurrentAnimation( m_mode, m_currentAnim ); } else if ( m_frameAnimCount > 0 ) { m_mode = Model::ANIMMODE_FRAME; m_currentAnim = indexToAnim( m_animName->currentIndex() ); m_model->setCurrentAnimation( m_mode, m_currentAnim ); } m_model->setCurrentAnimationFrame( m_currentFrame ); refreshPage(); m_model->operationComplete( tr( "Start animation mode", "operation complete" ).toUtf8() ); } m_stop->setEnabled( false ); } void AnimWidget::nameSelected( int index ) { log_debug( "anim name selected: %d\n", index ); if ( !m_ignoreChange && index >= (int) m_animCount ) { NewAnim win( this ); g_prefs.setDefault( "ui_new_anim_type", 0 ); win.setSkeletal( g_prefs( "ui_new_anim_type" ).intValue() == 0 ); if ( win.exec() ) { stopClicked(); g_prefs( "ui_new_anim_type" ) = win.isSkeletal() ? 0 : 1; QString name = win.getAnimName(); Model::AnimationModeE mode = win.isSkeletal() ? Model::ANIMMODE_SKELETAL : Model::ANIMMODE_FRAME; unsigned frameCount = win.getAnimFrameCount(); double fps = win.getAnimFPS(); bool loop = win.getAnimLooping(); int anim = m_model->addAnimation( mode, name.toUtf8() ); m_model->setAnimFrameCount( mode, anim, frameCount ); m_model->setAnimFPS( mode, anim, fps ); m_model->setAnimLooping( mode, anim, loop ); m_model->setCurrentAnimation( mode, anim ); m_model->operationComplete( tr( "New Animation", "operation complete" ).toUtf8() ); initialize( m_model, true ); return; } else { if ( m_animCount > 0 ) { index = animToIndex( m_mode, m_currentAnim ); m_animName->setCurrentIndex( index ); } else { return; } } } m_currentTime = 0; m_mode = indexToMode( index ); index = indexToAnim( index ); m_currentAnim = index; m_currentFrame = 0; if( index < (int) m_animCount ) { m_model->setCurrentAnimation( m_mode, m_currentAnim ); if ( m_playing ) { // Re-initialize time interval based on new anim's FPS. doPlay(); } } else { m_model->setCurrentAnimation( m_mode, (unsigned) 0 ); } refreshPage(); } void AnimWidget::setCurrentFrame( int frame ) { if ( m_model->setCurrentAnimationFrame( frame - 1 ) ) { m_currentFrame = frame - 1; QString str = tr("Frame: "); QString numStr = QString::asprintf( "%03d", frame ); m_countLabel->setText( str + numStr ); } else { m_countLabel->setText( tr( "Frame: n/a" ) ); } DecalManager::getInstance()->modelUpdated( m_model ); } void AnimWidget::editClicked() { NewAnim win( this ); win.editMode(); win.setSkeletal( m_mode == Model::ANIMMODE_SKELETAL ); win.setAnimName( m_model->getAnimName( m_mode, m_currentAnim ) ); win.setAnimFrameCount( m_model->getAnimFrameCount( m_mode, m_currentAnim ) ); win.setAnimFPS( m_model->getAnimFPS( m_mode, m_currentAnim ) ); win.setAnimLooping( m_model->getAnimLooping( m_mode, m_currentAnim ) ); if ( win.exec() ) { QString name = win.getAnimName(); unsigned frameCount = win.getAnimFrameCount(); double fps = win.getAnimFPS(); bool loop = win.getAnimLooping(); m_model->setAnimName( m_mode, m_currentAnim, name.toUtf8() ); m_model->setAnimFrameCount( m_mode, m_currentAnim, frameCount ); m_model->setAnimFPS( m_mode, m_currentAnim, fps ); m_model->setAnimLooping( m_mode, m_currentAnim, loop ); m_model->operationComplete( tr( "Edit Animation", "operation complete" ).toUtf8() ); refreshPage(); return; } } void AnimWidget::deleteClicked() { int index = m_animName->currentIndex(); if ( (unsigned int) index < m_animCount ) { if ( QMessageBox::Ok == QMessageBox::warning( this, tr("Delete Animation?", "window title"), tr("Are you sure you want to delete this animation?"), QMessageBox::Ok, QMessageBox::Cancel ) ) { m_ignoreChange = true; Model::AnimationModeE mode = indexToMode( index ); int anim = indexToAnim( index ); m_model->deleteAnimation( mode, anim ); refreshPage(); m_ignoreChange = true; insertAnimationNames(); index--; if ( index < 0 ) { index = 0; } m_animName->setCurrentIndex( index ); nameSelected( index ); m_ignoreChange = false; m_model->operationComplete( tr( "Delete Animation", "Delete animation, operation complete" ).toUtf8() ); } } } void AnimWidget::changeFPS() { if ( !m_ignoreChange && m_animCount > 0 ) { log_debug( "changing FPS\n" ); m_model->setAnimFPS( m_mode, indexToAnim( m_animName->currentIndex() ), m_fps->text().toDouble() ); m_model->operationComplete( tr( "Set FPS", "Frames per second, operation complete" ).toUtf8() ); } } void AnimWidget::changeFrameCount() { if ( !m_ignoreChange ) { if ( m_animCount > 0 ) { m_model->setAnimFrameCount( m_mode, indexToAnim( m_animName->currentIndex() ), m_frameCount->value() ); m_model->operationComplete( tr( "Change Frame Count", "operation complete" ).toUtf8() ); m_countSlider->setMinimum( 1 ); m_countSlider->setValue( m_model->getCurrentAnimationFrame() + 1 ); setCurrentFrame( m_model->getCurrentAnimationFrame() + 1 ); m_countSlider->setMaximum( m_frameCount->value() ); m_countSlider->update(); DecalManager::getInstance()->modelAnimate( m_model ); } } } void AnimWidget::clearFrame() { Model::AnimationModeE mode = m_model->getAnimationMode(); if ( mode == Model::ANIMMODE_FRAME || mode == Model::ANIMMODE_SKELETAL ) { int anim = m_model->getCurrentAnimation(); int frame = m_model->getCurrentAnimationFrame(); m_model->clearAnimFrame( mode, anim, frame ); m_model->operationComplete( tr( "Clear frame", "Remove animation data from frame, operation complete" ).toUtf8() ); DecalManager::getInstance()->modelUpdated( m_model ); } } bool AnimWidget::copyFrame( bool selected ) { Model::AnimationModeE mode = m_model->getAnimationMode(); m_frameCopyList.clear(); m_framePointCopyList.clear(); m_keyframeCopyList.clear(); if ( mode == Model::ANIMMODE_FRAME ) { unsigned numVertices = m_model->getVertexCount(); unsigned numPoints = m_model->getPointCount(); unsigned v = 0; FrameCopy copy; copy.vertex = 0; copy.x = 0; copy.y = 0; copy.z = 0; FramePointCopy copyPoint; copyPoint.point = 0; copyPoint.x = 0; copyPoint.y = 0; copyPoint.z = 0; copyPoint.rx = 0; copyPoint.ry = 0; copyPoint.rz = 0; for ( v = 0; v < numVertices; v++ ) { if ( !selected || m_model->isVertexSelected(v) ) { if ( m_model->getFrameAnimVertexCoords( m_currentAnim, m_currentFrame, v, copy.x, copy.y, copy.z ) ) { copy.vertex = v; m_frameCopyList.push_back( copy ); } } } for ( v = 0; v < numPoints; v++ ) { if ( !selected || m_model->isPointSelected(v) ) { if ( m_model->getFrameAnimPointCoords( m_currentAnim, m_currentFrame, v, copyPoint.x, copyPoint.y, copyPoint.z ) && m_model->getFrameAnimPointRotation( m_currentAnim, m_currentFrame, v, copyPoint.rx, copyPoint.ry, copyPoint.rz ) ) { copyPoint.point = v; m_framePointCopyList.push_back( copyPoint ); } } } return ( m_frameCopyList.size() > 0 ? true : false ); } else if ( mode == Model::ANIMMODE_SKELETAL ) { unsigned numJoints = m_model->getBoneJointCount(); unsigned j = 0; KeyframeCopy copy; copy.joint = 0; copy.x = 0; copy.y = 0; copy.z = 0; copy.isRotation = false; bool loop = m_model->getAnimLooping( Model::ANIMMODE_SKELETAL, m_currentAnim ); for ( j = 0; j < numJoints; j++ ) { if ( !selected || m_model->isBoneJointSelected(j) ) { if ( m_model->interpSkelAnimKeyframe( m_currentAnim, m_currentFrame, loop, j, false, copy.x, copy.y, copy.z ) ) { copy.joint = j; copy.isRotation = false; m_keyframeCopyList.push_back( copy ); } } } for ( j = 0; j < numJoints; j++ ) { if ( !selected || m_model->isBoneJointSelected(j) ) { if ( m_model->interpSkelAnimKeyframe( m_currentAnim, m_currentFrame, loop, j, true, copy.x, copy.y, copy.z ) ) { copy.joint = j; copy.isRotation = true; m_keyframeCopyList.push_back( copy ); } } } return ( m_keyframeCopyList.size() > 0 ? true : false ); } return false; } void AnimWidget::pasteFrame() { Model::AnimationModeE mode = m_model->getAnimationMode(); if ( mode == Model::ANIMMODE_FRAME ) { if ( m_frameCopyList.empty() && m_framePointCopyList.empty() ) { msg_error( tr("No frame animation data to paste").toUtf8() ); return; } m_model->unselectAll(); m_model->beginSelectionDifference(); FrameCopyList::iterator it; FramePointCopyList::iterator pit; for ( it = m_frameCopyList.begin(); it != m_frameCopyList.end(); it++ ) { m_model->selectVertex( (*it).vertex ); m_model->setFrameAnimVertexCoords( m_currentAnim, m_currentFrame, (*it).vertex, (*it).x, (*it).y, (*it).z ); } for ( pit = m_framePointCopyList.begin(); pit != m_framePointCopyList.end(); pit++ ) { m_model->selectPoint( (*pit).point ); m_model->setFrameAnimPointCoords( m_currentAnim, m_currentFrame, (*pit).point, (*pit).x, (*pit).y, (*pit).z ); m_model->setFrameAnimPointRotation( m_currentAnim, m_currentFrame, (*pit).point, (*pit).rx, (*pit).ry, (*pit).rz ); } m_model->endSelectionDifference(); m_model->operationComplete( tr( "Paste frame", "paste frame animation position, operation complete" ).toUtf8() ); m_model->setCurrentAnimationFrame( m_currentFrame ); DecalManager::getInstance()->modelUpdated( m_model ); } else if ( mode == Model::ANIMMODE_SKELETAL ) { KeyframeCopyList::iterator it; if ( m_keyframeCopyList.empty() ) { msg_error( tr("No skeletal animation data to paste").toUtf8() ); return; } m_model->unselectAll(); m_model->beginSelectionDifference(); bool loop = m_model->getAnimLooping( Model::ANIMMODE_SKELETAL, m_currentAnim ); for ( it = m_keyframeCopyList.begin(); it != m_keyframeCopyList.end(); it++ ) { double pasteVec[3] = { (*it).x, (*it).y, (*it).z }; double currentVec[3]; m_model->interpSkelAnimKeyframe( m_currentAnim, m_currentFrame, loop, (*it).joint, (*it).isRotation, currentVec[0], currentVec[1], currentVec[2] ); if ( !floatCompareVector( currentVec, pasteVec, 3 ) ) { m_model->selectBoneJoint( (*it).joint ); m_model->setSkelAnimKeyframe( m_currentAnim, m_currentFrame, (*it).joint, (*it).isRotation, (*it).x, (*it).y, (*it).z ); } } m_model->endSelectionDifference(); m_model->operationComplete( tr( "Paste keyframe", "Paste keyframe animation data complete" ).toUtf8() ); m_model->setCurrentAnimationFrame( m_currentFrame ); // Force refresh of joints DecalManager::getInstance()->modelUpdated( m_model ); } } void AnimWidget::playClicked() { if ( m_playing ) { doPause(); } else { doPlay(); } m_stop->setEnabled( true ); } void AnimWidget::stopClicked() { m_currentTime = 0; m_stop->setEnabled( false ); m_play->setEnabled(true); m_frameCount->setEnabled(true); m_countSlider->setEnabled(true); m_playing = false; m_animTimer->stop(); m_model->setCurrentAnimationFrame( m_countSlider->value() - 1 ); DecalManager::getInstance()->modelUpdated( m_model ); } void AnimWidget::loopToggled( bool o ) { if ( !m_ignoreChange && m_animCount > 0 ) { log_debug( "toggling loop\n" ); m_model->setAnimLooping( m_mode, indexToAnim( m_animName->currentIndex() ), o ); m_model->operationComplete( tr( "Set Looping", "Change whether animation loops operation complete" ).toUtf8() ); m_model->setCurrentAnimationFrame( m_countSlider->value() - 1 ); DecalManager::getInstance()->modelUpdated( m_model ); } } void AnimWidget::doPlay() { m_playing = true; if ( m_animCount == 0 ) { return; } m_play->setEnabled(false); m_frameCount->setEnabled(false); m_countSlider->setEnabled(false); m_timeInterval = double (1.0 / m_model->getAnimFPS( m_mode, indexToAnim( m_animName->currentIndex() ) )); const double shortInterval = 1.0 / 20.0; if ( m_timeInterval > shortInterval ) m_timeInterval = shortInterval; PORT_gettimeofday( &m_startTime ); m_animTimer->start( (int) (m_timeInterval * 1000) ); log_debug( "starting %s animation, update every %.03f seconds\n", (m_mode == Model::ANIMMODE_SKELETAL ? "skeletal" : "frame" ), m_timeInterval ); } void AnimWidget::doPause() { m_playing = false; m_play->setEnabled(true); m_animTimer->stop(); } void AnimWidget::timeElapsed() { PORT_timeval tv; PORT_gettimeofday( &tv ); unsigned t = (tv.tv_sec - m_startTime.tv_sec) * 1000 + (tv.tv_msec - m_startTime.tv_msec); m_currentTime = ((double) t) / 1000.0; if ( m_model->setCurrentAnimationTime( m_currentTime ) ) { //log_debug( "animation time: %f (%d)\n", m_currentTime, t ); } else { stopClicked(); //log_debug( "animation time: %f (complete)\n", m_currentTime ); } DecalManager::getInstance()->modelAnimate( m_model ); } void AnimWidget::undoRequest() { log_debug( "anim undo request\n" ); m_model->undo(); undoGuts(); } void AnimWidget::redoRequest() { log_debug( "anim redo request\n" ); m_model->redo(); undoGuts(); } void AnimWidget::undoGuts() { if ( !m_model->getAnimationMode() ) { stopAnimationMode(); return; } m_undoing = true; Model::AnimationModeE mode = m_model->inSkeletalMode() ? Model::ANIMMODE_SKELETAL : Model::ANIMMODE_FRAME; unsigned anim = m_model->getCurrentAnimation(); unsigned frame = m_model->getCurrentAnimationFrame(); insertAnimationNames(); m_animName->setCurrentIndex( animToIndex( mode, anim ) ); m_model->setCurrentAnimation( mode, anim ); m_model->setCurrentAnimationFrame( frame ); m_currentAnim = anim; m_currentFrame = frame; m_mode = mode; m_undoing = false; refreshPage(); DecalManager::getInstance()->modelUpdated( m_model ); } void AnimWidget::insertAnimationNames() { m_animName->clear(); unsigned int t; m_skelAnimCount = m_model->getAnimCount( Model::ANIMMODE_SKELETAL ); for ( t = 0; t < m_skelAnimCount; t++ ) { m_animName->insertItem( t, QString::fromUtf8( m_model->getAnimName( Model::ANIMMODE_SKELETAL, t )), t ); } m_frameAnimCount = m_model->getAnimCount( Model::ANIMMODE_FRAME ); for ( t = 0; t < m_frameAnimCount; t++ ) { m_animName->insertItem( m_skelAnimCount + t, QString( m_model->getAnimName( Model::ANIMMODE_FRAME, t )), t + m_skelAnimCount ); } m_animCount = m_skelAnimCount + m_frameAnimCount; m_animName->insertItem( m_animCount, tr( "" ), m_animCount ); } void AnimWidget::refreshPage() { log_debug( "refresh anim window page\n" ); if ( !m_undoing ) { if( m_animCount > 0 ) { int index = m_animName->currentIndex(); Model::AnimationModeE mode = indexToMode( index ); index = indexToAnim( index ); m_editButton->setEnabled( true ); m_deleteButton->setEnabled( true ); if ( m_playing ) { m_frameCount->setEnabled( false ); m_countSlider->setEnabled( false ); } else { m_frameCount->setEnabled( true ); m_countSlider->setEnabled( true ); } m_fps->setEnabled( true ); m_animName->setEnabled( true ); m_play->setEnabled( true ); m_loop->setEnabled( true ); unsigned count = m_model->getAnimFrameCount( mode, index ); m_ignoreChange = true; // Qt alerts us even if we're responsible m_frameCount->setValue( count ); m_fps->setText( QString::number(m_model->getAnimFPS( mode, index ) ) ); m_loop->setChecked( m_model->getAnimLooping( mode, index ) ); m_ignoreChange = false; m_countSlider->setMinimum( 1 ); m_countSlider->setMaximum( count ); m_countSlider->setValue( m_currentFrame + 1 ); m_countSlider->setTickInterval( getSliderTickInterval( count ) ); m_countSlider->update(); setCurrentFrame( m_currentFrame + 1 ); emit animValid(); } else { m_editButton->setEnabled( false ); m_deleteButton->setEnabled( false ); m_frameCount->setEnabled( false ); m_fps->setEnabled( false ); m_countSlider->setEnabled( false ); m_play->setEnabled( false ); m_stop->setEnabled( false ); m_loop->setEnabled( false ); m_fps->setText( QString("0") ); m_loop->setChecked( false ); m_ignoreChange = true; // Qt alerts us even if we're responsible m_frameCount->setValue( 0 ); m_ignoreChange = false; m_countSlider->setMinimum( 0 ); m_countSlider->setMaximum( 0 ); m_countSlider->setValue( 0 ); setCurrentFrame( 0 ); emit animInvalid(); } } else { if ( m_animCount > 0) { m_animName->setEnabled( true ); if ( m_playing ) { m_frameCount->setEnabled( false ); m_countSlider->setEnabled( false ); } else { m_frameCount->setEnabled( true ); m_countSlider->setEnabled( true ); } m_fps->setEnabled( true ); m_play->setEnabled( true ); m_stop->setEnabled( true ); m_loop->setEnabled( true ); } else { //m_animName->setEnabled( false ); m_frameCount->setEnabled( false ); m_fps->setEnabled( false ); m_countSlider->setEnabled( false ); m_play->setEnabled( false ); m_stop->setEnabled( false ); m_loop->setEnabled( false ); m_fps->setText( QString("0") ); m_loop->setChecked( false ); m_ignoreChange = true; // Qt alerts us even if we're responsible m_frameCount->setValue( 0 ); m_ignoreChange = false; m_countSlider->setMinimum( 0 ); m_countSlider->setMaximum( 0 ); m_countSlider->setValue( 0 ); } } } void AnimWidget::stopAnimationMode() { if ( m_needShutdown ) { if ( m_model->getAnimationMode() ) { m_model->setNoAnimation(); m_model->operationComplete( tr( "End animation mode", "operation complete" ).toUtf8() ); DecalManager::getInstance()->modelUpdated( m_model ); } emit animWindowClosed(); } m_needShutdown = false; } bool AnimWidget::indexIsSkel( int index ) { if ( index >= (int) m_skelAnimCount ) { return false; } else { return true; } } Model::AnimationModeE AnimWidget::indexToMode( int index ) { if ( index >= (int) m_skelAnimCount ) { return Model::ANIMMODE_FRAME; } else { return Model::ANIMMODE_SKELETAL; } } int AnimWidget::indexToAnim( int index ) { if ( index >= (int) m_skelAnimCount ) { return index -= m_skelAnimCount; } else { return index; } } int AnimWidget::animToIndex( Model::AnimationModeE mode, int anim ) { if ( mode == Model::ANIMMODE_FRAME ) { return anim + m_skelAnimCount; } else { return anim; } } mm3d-1.3.15/src/implui/animwidget.h000066400000000000000000000066521466047437300170570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ANIMWIDGET_H #define __ANIMWIDGET_H #include "animwidget.base.h" #include "mm3dport.h" #include "model.h" #include class QTimer; class AnimWidget : public QWidget, public Ui::AnimWidgetBase { Q_OBJECT public: AnimWidget( Model * model, bool isUndo, QWidget * parent = NULL ); virtual ~AnimWidget(); void initialize( Model * model, bool isUndo ); void stopAnimationMode(); void doPlay(); void doPause(); signals: void animWindowClosed(); void animInvalid(); void animValid(); public slots: void nameSelected(int); void editClicked(); void deleteClicked(); void setCurrentFrame(int); void changeFPS(); void changeFrameCount(); void timeElapsed(); void playClicked(); void stopClicked(); void loopToggled(bool); bool copyFrame( bool selected ); void pasteFrame(); void clearFrame(); void undoRequest(); void redoRequest(); void refreshPage(); protected: bool indexIsSkel( int index ); Model::AnimationModeE indexToMode( int index ); int indexToAnim( int index ); int animToIndex( Model::AnimationModeE mode, int anim ); void undoGuts(); void insertAnimationNames(); Model * m_model; bool m_playing; double m_timeInterval; double m_currentTime; unsigned m_skelAnimCount; unsigned m_frameAnimCount; unsigned m_animCount; unsigned m_currentAnim; unsigned m_currentFrame; Model::AnimationModeE m_mode; bool m_undoing; bool m_ignoreChange; bool m_needShutdown; PORT_timeval m_startTime; typedef struct _KeyframeCopy_t { unsigned joint; double x; double y; double z; bool isRotation; } KeyframeCopy; typedef struct _FrameCopy_t { unsigned vertex; double x; double y; double z; } FrameCopy; typedef struct _FramePointCopy_t { unsigned point; double x; double y; double z; double rx; double ry; double rz; } FramePointCopy; typedef std::list KeyframeCopyList; typedef std::list FrameCopyList; typedef std::list FramePointCopyList; KeyframeCopyList m_keyframeCopyList; FrameCopyList m_frameCopyList; FramePointCopyList m_framePointCopyList; QTimer * m_animTimer; }; #endif // __ANIMWIDGET_H mm3d-1.3.15/src/implui/animwin.cc000066400000000000000000000025031466047437300165160ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "animwin.h" #include "log.h" #include AnimWindow::AnimWindow( Model * model, bool isUndo, QWidget * parent ) : QDockWidget( tr("Animations"), parent ), m_animWidget( new AnimWidget( model, isUndo, this ) ) { setWidget( m_animWidget ); } AnimWindow::~AnimWindow() { log_debug( "Destroying AnimWindow()\n" ); } void AnimWindow::close() { QDockWidget::hide(); } void AnimWindow::closeEvent( QCloseEvent * e ) { emit animWindowClosed(); } mm3d-1.3.15/src/implui/animwin.h000066400000000000000000000026151466047437300163640ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef ANIMWIN_H_INC__ #define ANIMWIN_H_INC__ #include #include "animwidget.h" class Model; class AnimWindow : public QDockWidget { Q_OBJECT public: AnimWindow( Model * model, bool isUndo, QWidget * parent = NULL ); virtual ~AnimWindow(); AnimWidget * getAnimWidget() { return m_animWidget; } signals: void animWindowClosed(); public slots: void close(); protected: void closeEvent( QCloseEvent * e ); private: AnimWidget * m_animWidget; }; #endif // ANIMWIN_H_INC__ mm3d-1.3.15/src/implui/autoassignjointwin.cc000066400000000000000000000035601466047437300210170ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "autoassignjointwin.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "helpwin.h" #include #include #include using std::list; using std::map; AutoAssignJointWin::AutoAssignJointWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setModal( true ); setupUi( this ); if ( m_model->getSelectedBoneJointCount() == 0 ) { m_selected->setChecked( false ); m_selected->setEnabled( false ); } QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } AutoAssignJointWin::~AutoAssignJointWin() { } void AutoAssignJointWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_autoassignjointwin.html", true ); win->show(); } int AutoAssignJointWin::getSensitivity() { return m_sensitivity->value(); } bool AutoAssignJointWin::getSelected() { return m_selected->isChecked(); } mm3d-1.3.15/src/implui/autoassignjointwin.h000066400000000000000000000025501466047437300206570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __AUTOASSIGNJOINTWIN_H #define __AUTOASSIGNJOINTWIN_H #include "autoassignjointwin.base.h" #include class Model; class AutoAssignJointWin : public QDialog, public Ui::AutoAssignJointWinBase { Q_OBJECT public: AutoAssignJointWin( Model *, QWidget * parent = NULL ); virtual ~AutoAssignJointWin(); int getSensitivity(); bool getSelected(); public slots: void helpNowEvent(); protected: Model * m_model; }; #endif // __AUTOASSIGNJOINTWIN_H mm3d-1.3.15/src/implui/backgroundselect.cc000066400000000000000000000075271466047437300204060ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "backgroundselect.h" #include "model.h" #include "log.h" #include "msg.h" #include "decalmgr.h" #include "textureframe.h" #include "3dmprefs.h" #include "texmgr.h" #include "texture.h" #include "model.h" #include "errorobj.h" #include #include #include #include #include using std::list; using std::map; BackgroundSelect::BackgroundSelect( Model * model, unsigned index, QWidget * parent ) : QWidget( parent ), m_model( model ), m_index( index ) { setupUi( this ); std::string filename = m_model->getBackgroundImage( index ); setFilename( filename.c_str() ); m_textureFrame->updateSize(); } BackgroundSelect::~BackgroundSelect() { } void BackgroundSelect::noneEvent() { m_model->setBackgroundImage( m_index, "" ); m_textureFrame->setTexture( -1, NULL ); m_textureFrame->updateSize(); } void BackgroundSelect::selectFileEvent() { list formats = TextureManager::getInstance()->getAllReadTypes(); QString formatsStr = tr( "All Supported Formats (" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += " "; } } formatsStr += ")"; QString dir = QString::fromUtf8( g_prefs( "ui_background_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = "."; } QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; All Files (*)" ) ); d.setWindowTitle( tr("Open background image") ); d.selectNameFilter( formatsStr ); int execval = d.exec(); QStringList files = d.selectedFiles(); if ( QDialog::Accepted == execval && !files.empty() ) { std::string file = (const char *) files[0].toUtf8(); QString path = d.directory().absolutePath(); g_prefs( "ui_background_dir" ) = (const char *) path.toUtf8(); Texture * tex = TextureManager::getInstance()->getTexture( file.c_str() ); if ( tex ) { m_model->setBackgroundImage( m_index, file.c_str() ); m_textureFrame->setTexture( -1, tex ); m_textureFrame->updateSize(); // Do NOT delete tex, TextureManager does that } else { QString err = tr(file.c_str()) + "\n"; Texture::ErrorE e = TextureManager::getInstance()->getLastError(); if ( e != Texture::ERROR_NONE ) { err += textureErrStr( e ); } else { err += tr("Could not open file"); } msg_error( (const char *) err.toUtf8() ); } } } void BackgroundSelect::setFilename( const char * filename ) { Texture * tex = TextureManager::getInstance()->getTexture( filename ); if ( tex ) { m_model->setBackgroundImage( m_index, filename ); m_textureFrame->setTexture( -1, tex ); m_textureFrame->updateSize(); // Do NOT delete tex, TextureManager does that } } mm3d-1.3.15/src/implui/backgroundselect.h000066400000000000000000000026361466047437300202440ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BACKGROUNDSELECT_H #define __BACKGROUNDSELECT_H #include "backgroundselect.base.h" #include class Model; class BackgroundSelect : public QWidget, public Ui::BackgroundSelectBase { Q_OBJECT public: BackgroundSelect( Model * mode, unsigned index, QWidget * parent = NULL ); virtual ~BackgroundSelect(); public slots: void setFilename( const char * filename ); void noneEvent(); void selectFileEvent(); protected: Model * m_model; unsigned m_index; }; #endif // __BACKGROUNDSELECT_H mm3d-1.3.15/src/implui/backgroundwin.cc000066400000000000000000000047601466047437300177200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "backgroundwin.h" #include "backgroundselect.h" #include "viewpanel.h" #include "model.h" #include "log.h" #include "decalmgr.h" #include "textureframe.h" #include "helpwin.h" #include #include #include #include #include #include using std::list; using std::map; BackgroundWin::BackgroundWin( Model * model, ViewPanel * viewPanel, QWidget * parent ) : QDialog( parent ), m_model( model ), m_viewPanel( viewPanel ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); for ( unsigned t = 0; t < 6; t++ ) { QWidget * p = m_tabs->widget( t ); QHBoxLayout * l = new QHBoxLayout( p ); m_bgSelect[t] = new BackgroundSelect( m_model, t, p ); l->addWidget( m_bgSelect[t] ); } QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } BackgroundWin::~BackgroundWin() { } void BackgroundWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_backgroundwin.html", true ); win->show(); } void BackgroundWin::selectedPageEvent( int index ) { QWidget * widget = m_tabs->currentWidget(); widget->repaint(); } void BackgroundWin::accept() { if ( m_viewPanel ) { m_viewPanel->modelUpdatedEvent(); } m_model->operationComplete( tr( "Background Image", "operation complete" ).toUtf8() ); QDialog::accept(); } void BackgroundWin::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/backgroundwin.h000066400000000000000000000027451466047437300175630ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BACKGROUNDWIN_H #define __BACKGROUNDWIN_H #include "backgroundwin.base.h" #include class ViewPanel; class Model; class BackgroundSelect; class BackgroundWin : public QDialog, public Ui::BackgroundWinBase { Q_OBJECT public: BackgroundWin( Model *, ViewPanel * viewPanel, QWidget * parent = NULL ); virtual ~BackgroundWin(); public slots: void selectedPageEvent( int index ); void accept(); void reject(); void helpNowEvent(); protected: Model * m_model; ViewPanel * m_viewPanel; BackgroundSelect * m_bgSelect[6]; }; #endif // __BACKGROUNDWIN_H mm3d-1.3.15/src/implui/boolpanel.cc000066400000000000000000000037011466047437300170300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "boolpanel.h" #include "viewpanel.h" #include "boolwin.h" #include "log.h" #include #include BoolPanel::BoolPanel( Model * model, QWidget * parent, ViewPanel * panel ) : QDockWidget( tr("Boolean Operation"), parent ), m_panel( panel ), m_boolWidget( new BoolWin( model, panel, this ) ) { setWidget( m_boolWidget ); } BoolPanel::~BoolPanel() { } void BoolPanel::setModel( Model * model ) { m_boolWidget->setModel( model ); } void BoolPanel::modelChanged( int changeBits ) { if ( this->isVisible() ) { } } void BoolPanel::show() { QDockWidget::show(); // this is causing a segfault on dock/undock because the widgets // are being destroyed and re-created. The solution is to call setModel // explicitly whenever the window is shown (this only happens in viewwin.cc) //setModel( m_model ) } void BoolPanel::close() { hide(); // Do hide instead } void BoolPanel::hide() { log_debug( "BoolPanel::hide()\n" ); QDockWidget::hide(); } void BoolPanel::contextMenuEvent( QContextMenuEvent * e ) { e->ignore(); } mm3d-1.3.15/src/implui/boolpanel.h000066400000000000000000000031431466047437300166720ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BOOLPANEL_H #define __BOOLPANEL_H #include "contextwidget.h" #include "model.h" #include class QContextMenuEvent; class ViewPanel; class BoolWin; class BoolPanel : public QDockWidget, public Model::Observer { Q_OBJECT public: BoolPanel( Model * model, QWidget * parent, ViewPanel * panel ); virtual ~BoolPanel(); // QDockWindow methods public slots: void show(); void close(); void hide(); void setModel( Model * m ); // Model::Observer methods void modelChanged( int changeBits ); protected: void contextMenuEvent( QContextMenuEvent * e ); Model * m_model; ViewPanel * m_panel; BoolWin * m_boolWidget; }; #endif // __BOOLPANEL_H mm3d-1.3.15/src/implui/boolwin.cc000066400000000000000000000114401466047437300165250ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "boolwin.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "decalmgr.h" #include "helpwin.h" #include "viewpanel.h" #include "modelstatus.h" #include #include #include #include #include using std::list; BoolWin::BoolWin( Model * model, ViewPanel * panel, QWidget * parent ) : QWidget( parent ), m_model( model ), m_panel( panel ), m_operation( Model::BO_Union ) { setupUi( this ); m_unionButton->setChecked( true ); updateOperationButton(); clearObject(); } BoolWin::~BoolWin() { } void BoolWin::setModel( Model * m ) { m_model = m; clearObject(); } void BoolWin::operationChangedEvent( bool o) { if ( o ) { if ( m_fuseButton->isChecked() ) { m_operation = Model::BO_Union; } else if ( m_unionButton->isChecked() ) { m_operation = Model::BO_UnionRemove; } else if ( m_subtractButton->isChecked() ) { m_operation = Model::BO_Subtraction; } else if ( m_intersectButton->isChecked() ) { m_operation = Model::BO_Intersection; } updateOperationButton(); } } void BoolWin::doOperationEvent() { std::list al; std::list bl; unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { bl.push_back( t ); } else if ( m_model->isTriangleMarked( t ) ) { al.push_back( t ); } } if ( !al.empty() && !bl.empty() ) { m_model->booleanOperation( m_operation, al, bl ); QString opStr; switch ( m_operation ) { case Model::BO_UnionRemove: opStr = tr( "Union", "boolean operation" ); break; case Model::BO_Subtraction: opStr = tr( "Subtraction", "boolean operation" ); break; case Model::BO_Intersection: opStr = tr( "Intersection", "boolean operation" ); break; case Model::BO_Union: default: opStr = tr( "Union", "boolean operation" ); break; } m_model->operationComplete( opStr.toUtf8() ); clearObject(); m_panel->modelUpdatedEvent(); } else { if ( bl.empty() ) { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", tr("You must have at least once face selected").toUtf8().data() ); } else { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", tr("Object A triangles are still selected").toUtf8().data() ); } } } void BoolWin::setObjectEvent() { std::list tris; m_model->getSelectedTriangles( tris ); if ( !tris.empty() ) { m_opButton->setEnabled( true ); QString labelStr = QString::asprintf( "Object: %d Faces", (int) tris.size() ); m_setLabel->setText( labelStr ); m_model->clearMarkedTriangles(); std::list::iterator it; for ( it = tris.begin(); it != tris.end(); it++ ) { m_model->setTriangleMarked( *it, true ); } } else { m_opButton->setEnabled( false ); } } void BoolWin::updateOperationButton() { switch ( m_operation ) { case Model::BO_UnionRemove: m_opButton->setText( tr( "Union With Selected", "boolean operation" ) ); break; case Model::BO_Subtraction: m_opButton->setText( tr( "Subtract Selected", "boolean operation" ) ); break; case Model::BO_Intersection: m_opButton->setText( tr( "Intersect With Selected", "boolean operation" ) ); break; case Model::BO_Union: default: m_opButton->setText( tr( "Fuse Selected", "boolean operation" ) ); break; } } void BoolWin::clearObject() { m_setLabel->setText( tr( "Select faces to set", "Select faces to set as 'A' Object in boolean operation" ) ); m_opButton->setEnabled( false ); } mm3d-1.3.15/src/implui/boolwin.h000066400000000000000000000027561466047437300164010ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BOOLWIN_H #define __BOOLWIN_H #include "boolwin.base.h" #include "model.h" #include class ViewPanel; class BoolWin : public QWidget, public Ui::BoolWinBase { Q_OBJECT public: BoolWin( Model *, ViewPanel * panel, QWidget * parent = NULL ); virtual ~BoolWin(); void setModel( Model * ); public slots: void operationChangedEvent( bool ); void doOperationEvent(); void setObjectEvent(); protected: void updateOperationButton(); void clearObject(); Model * m_model; ViewPanel * m_panel; Model::BooleanOpE m_operation; }; #endif // __BOOLWIN_H mm3d-1.3.15/src/implui/cal3dprompt.cc000066400000000000000000000051671466047437300173150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "cal3dprompt.h" #include "cal3dfilter.h" #include "model.h" #include "helpwin.h" #include #include Cal3dPrompt::Cal3dPrompt() : QDialog( NULL ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } Cal3dPrompt::~Cal3dPrompt() { } void Cal3dPrompt::setOptions( Cal3dFilter::Cal3dOptions * opts ) { if ( opts->m_singleMeshFile ) m_singleMeshFile->setChecked( true ); else m_separateMeshFiles->setChecked( true ); if ( opts->m_xmlMatFile ) m_xmlMatFile->setChecked( true ); else m_binaryMatFile->setChecked( true ); } void Cal3dPrompt::getOptions( Cal3dFilter::Cal3dOptions * opts ) { opts->m_singleMeshFile = m_singleMeshFile->isChecked(); opts->m_xmlMatFile = m_xmlMatFile->isChecked(); } void Cal3dPrompt::helpNowEvent() { HelpWin * win = new HelpWin( "olh_cal3dprompt.html", true ); win->show(); } // This function takes a ModelFilter::Options argument, downcasts it // to an Cal3dOptions cal3dect, and uses it to prompt the user for // options for the Cal3d file filter. // // This function is registered with the Cal3dFilter class when the // filter is created in stdfilters.cc. bool cal3dprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ) { bool rval = false; Cal3dPrompt p; Cal3dFilter::Cal3dOptions * opts = dynamic_cast< Cal3dFilter::Cal3dOptions * >( o ); if ( opts ) { opts->setOptionsFromModel( model ); p.setOptions( opts ); if ( p.exec() ) { rval = true; p.getOptions( opts ); } } else { rval = true; } return rval; } mm3d-1.3.15/src/implui/cal3dprompt.h000066400000000000000000000027021466047437300171470ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CAL3DPROMPT_H #define __CAL3DPROMPT_H #include "cal3dprompt.base.h" #include "modelfilter.h" #include "cal3dfilter.h" #include class Model; class Cal3dPrompt : public QDialog, public Ui::Cal3dPromptBase { Q_OBJECT public: Cal3dPrompt(); virtual ~Cal3dPrompt(); void setOptions( Cal3dFilter::Cal3dOptions * o ); void getOptions( Cal3dFilter::Cal3dOptions * o ); public slots: void helpNowEvent(); protected: }; bool cal3dprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ); #endif // __CAL3DPROMPT_H mm3d-1.3.15/src/implui/contextgroup.cc000066400000000000000000000174461466047437300176310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextgroup.h" #include "model.h" #include "contextpanelobserver.h" #include "groupwin.h" #include "texwin.h" #include "projectionwin.h" #include #include #include #include #include #include ContextGroup::ContextGroup( QWidget * parent, ContextPanelObserver * ob ) : QWidget( parent ), m_model( NULL ), m_observer( ob ), m_change( false ), m_update( false ) { setupUi( this ); } ContextGroup::~ContextGroup() { } void ContextGroup::setModel( Model * m ) { m_model = m; modelChanged( ~0 ); } void ContextGroup::modelChanged( int changeBits ) { // Only change if it's a group change or a selection change if ( (changeBits & Model::AddOther) || (changeBits & Model::SelectionChange ) ) { if ( !m_update ) { m_change = true; // Update group fields m_groupValue->clear(); m_groupValue->insertItem( 0, tr( "" ) ); m_materialValue->clear(); m_materialValue->insertItem( 0, tr( "" ) ); m_projectionValue->clear(); m_projectionValue->insertItem( 0, tr( "" ) ); unsigned int gcount = m_model->getGroupCount(); for ( unsigned int g = 0; g < gcount; g++ ) { m_groupValue->insertItem( g + 1, QString::fromUtf8( m_model->getGroupName( g ) ) ); } m_groupValue->insertItem( m_groupValue->count(), tr( "" ) ); unsigned int mcount = m_model->getTextureCount(); for ( unsigned int m = 0; m < mcount; m++ ) { m_materialValue->insertItem( m + 1, QString::fromUtf8( m_model->getTextureName( m ) ) ); } unsigned int pcount = m_model->getProjectionCount(); for ( unsigned int p = 0; p < pcount; p++ ) { m_projectionValue->insertItem( p + 1, QString::fromUtf8( m_model->getProjectionName( p ) ) ); } int group = -1; int proj = -1; unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; (group < 0 || proj < 0) && t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { if ( group < 0 ) { group = m_model->getTriangleGroup( t ); } if ( proj < 0 ) { proj = m_model->getTriangleProjection( t ); } } } if ( group >= 0 ) { m_materialValue->setEnabled( true ); m_materialProperties->setEnabled( true ); m_groupValue->setCurrentIndex( group + 1 ); int texId = m_model->getGroupTextureId( group ); if ( texId >= 0 ) { m_materialValue->setCurrentIndex( texId + 1 ); } } else { m_materialValue->setEnabled( false ); m_materialProperties->setEnabled( false ); } if ( proj >= 0 ) { m_projectionProperties->setEnabled( true ); m_projectionValue->setCurrentIndex( proj + 1 ); } else { m_projectionProperties->setEnabled( false ); } m_change = false; } } m_lastGroup = m_groupValue->currentIndex(); } void ContextGroup::groupChanged() { if ( !m_change ) { m_update = true; int g = m_groupValue->currentIndex() - 1; if ( g >= 0 ) { m_materialValue->setEnabled( true ); m_materialProperties->setEnabled( true ); bool addSelected = true; if ( g >= m_model->getGroupCount() ) { // TODO pick unique name? QString groupName = QInputDialog::getText( this, tr("New Group", "Name of new group, window title" ), tr("Enter new group name:"), QLineEdit::Normal, QString(), &addSelected ); if ( groupName.length() == 0 ) { addSelected = false; } if ( addSelected ) { m_model->addGroup( groupName.toUtf8() ); m_groupValue->setItemText( g + 1, groupName ); m_groupValue->insertItem( m_groupValue->count(), tr( "" ) ); } else { m_groupValue->setCurrentIndex( m_lastGroup ); } } if ( addSelected ) { m_model->addSelectedToGroup( g ); m_model->operationComplete( tr( "Set Group", "operation complete" ).toUtf8() ); } int texId = m_model->getGroupTextureId( g ); if ( texId >= 0 ) { m_materialValue->setCurrentIndex( texId + 1 ); } else { m_materialValue->setCurrentIndex( 0 ); } } else { m_materialValue->setEnabled( false ); m_materialProperties->setEnabled( false ); unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { g = m_model->getTriangleGroup( t ); if ( g >= 0 ) { m_model->removeTriangleFromGroup( g, t ); } } } m_model->operationComplete( tr( "Unset Group", "operation complete" ).toUtf8() ); } emit panelChange(); m_update = false; } } void ContextGroup::materialChanged() { if ( !m_change ) { m_update = true; int g = m_groupValue->currentIndex() - 1; int m = m_materialValue->currentIndex() - 1; if ( g >= 0 ) { m_model->setGroupTextureId( g, m ); m_model->operationComplete( tr( "Set Material", "operation complete" ).toUtf8() ); } emit panelChange(); m_update = false; } } void ContextGroup::projectionChanged() { if ( !m_change ) { m_update = true; int proj = m_projectionValue->currentIndex() - 1; unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { m_model->setTriangleProjection( t, proj ); } } if ( proj >= 0 ) { m_model->applyProjection( proj ); m_projectionProperties->setEnabled( true ); } else { m_projectionProperties->setEnabled( false ); } m_model->operationComplete( tr( "Set Projection", "operation complete" ).toUtf8() ); emit panelChange(); m_update = false; } } void ContextGroup::groupPropertiesClicked() { GroupWindow * win = new GroupWindow( m_model ); win->show(); } void ContextGroup::materialPropertiesClicked() { TextureWindow * win = new TextureWindow( m_model ); win->show(); } void ContextGroup::projectionPropertiesClicked() { m_observer->showProjectionEvent(); } mm3d-1.3.15/src/implui/contextgroup.h000066400000000000000000000035271466047437300174660ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTGROUP_H #define __CONTEXTGROUP_H class ContextPanelObserver; #include "contextgroup.base.h" #include "contextwidget.h" #include class Model; class ContextGroup : public QWidget, public Ui::ContextGroupBase, public ContextWidget { Q_OBJECT public: ContextGroup( QWidget * parent, ContextPanelObserver * ob ); virtual ~ContextGroup(); // ContextWidget methods void setModel( Model * ); void modelChanged( int changeBits ); bool isUpdating() { return m_update; }; signals: void panelChange(); public slots: // Group slots void groupChanged(); void groupPropertiesClicked(); void projectionChanged(); void projectionPropertiesClicked(); void materialChanged(); void materialPropertiesClicked(); protected: Model * m_model; ContextPanelObserver * m_observer; bool m_change; bool m_update; int m_lastGroup; }; #endif // __CONTEXTGROUP_H mm3d-1.3.15/src/implui/contextinfluences.cc000066400000000000000000000434551466047437300206270ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextinfluences.h" #include "groupwin.h" #include "texwin.h" #include "config.h" #include "log.h" #include #include #include #include #include ContextInfluences::JointCount::JointCount() : inList( false ), count( 0 ), typeIndex( Model::IT_Auto + 1 ), weight( 0 ) { for ( int t = 0; t < Model::IT_MAX; t++ ) { typeCount[t] = 0; } } ContextInfluences::ContextInfluences( QWidget * parent ) : QWidget( parent ), m_change( false ), m_update( false ) { setupUi( this ); } ContextInfluences::~ContextInfluences() { } void ContextInfluences::setModel( Model * m ) { m_model = m; modelChanged( ~0 ); } void ContextInfluences::modelChanged( int changeBits ) { // Only change if it's a group change or a selection change if ( (changeBits & Model::AddOther) || (changeBits & Model::SelectionChange ) ) { if ( !m_update ) { m_change = true; m_joint1->clear(); m_joint2->clear(); m_joint3->clear(); m_joint4->clear(); m_joint1->insertItem( 0, tr("") ); m_joint2->insertItem( 0, tr("") ); m_joint3->insertItem( 0, tr("") ); m_joint4->insertItem( 0, tr("") ); int bcount = m_model->getBoneJointCount(); for ( int b = 0; b < bcount; b++ ) { QString name = QString::fromUtf8( m_model->getBoneJointName( b ) ); m_joint1->insertItem( b+1, name ); m_joint2->insertItem( b+1, name ); m_joint3->insertItem( b+1, name ); m_joint4->insertItem( b+1, name ); } m_joint1->setCurrentIndex( 0 ); m_joint2->setCurrentIndex( 0 ); m_joint3->setCurrentIndex( 0 ); m_joint4->setCurrentIndex( 0 ); m_joints[0] = -1; m_joints[1] = -1; m_joints[2] = -1; m_joints[3] = -1; m_weight1->setEnabled( false ); m_weight2->setEnabled( false ); m_weight3->setEnabled( false ); m_weight4->setEnabled( false ); if ( m_model->getAnimationMode() != Model::ANIMMODE_NONE ) { m_joint1->setEnabled( false ); m_joint2->setEnabled( false ); m_joint3->setEnabled( false ); m_joint4->setEnabled( false ); } // Update influence fields m_jclist.clear(); m_jclist.resize( bcount ); list< Model::Position > plist; list< Model::Position >::iterator pit; Model::InfluenceList ilist; Model::InfluenceList::iterator iit; m_model->getSelectedPositions( plist ); // for now just do a sum on which bone joints are used the most // TODO: may want to weight by influence later for ( pit = plist.begin(); pit != plist.end(); pit++ ) { ilist.clear(); m_model->getPositionInfluences( (*pit), ilist ); for ( iit = ilist.begin(); iit != ilist.end(); iit++ ) { int bone = (*iit).m_boneId; m_jclist[ bone ].count += 1; m_jclist[ bone ].weight += (int) lround( (*iit).m_weight * 100.0 ); m_jclist[ bone ].typeCount[ (*iit).m_type ] += 1; } } for ( int joint = 0; joint < bcount; joint++ ) { int typeIndex = -1; for ( int t = 0; typeIndex != 0 && t < Model::IT_MAX; t++ ) { if ( m_jclist[ joint ].typeCount[ t ] > 0 ) { // If type index is unset, set it to the combo box // index for our type (off by one). If type index // is already set, it's mixed (index 0) typeIndex = ( typeIndex < 0 ) ? t + 1 : 0; } } if ( typeIndex >= 0 ) { m_jclist[ joint ].typeIndex = typeIndex; m_jclist[ joint ].weight = (int) lround( m_jclist[ joint ].weight / (double) m_jclist[ joint ].count ); } else { m_jclist[ joint ].weight = 100; } } for ( int index = 0; index < Model::MAX_INFLUENCES; index++ ) { int maxVal = 0; int joint = -1; for ( int b = 0; b < bcount; b++ ) { if ( !m_jclist[ b ].inList && m_jclist[ b ].count > maxVal ) { maxVal = m_jclist[b].count; joint = b; } } if ( maxVal > 0 ) { QComboBox * jointBox = m_joint1; switch ( index ) { case 0: jointBox = m_joint1; break; case 1: jointBox = m_joint2; break; case 2: jointBox = m_joint3; break; case 3: jointBox = m_joint4; break; default: break; } jointBox->setCurrentIndex( joint + 1 ); updateWeightField( index, true, m_jclist[ joint ].typeIndex, m_jclist[ joint ].weight ); //updateRemainders(); m_joints[index] = joint; log_debug( "joint index %d is joint ID %d\n", index, joint ); m_jclist[ joint ].inList = true; } else { // No more influenes, we're done index = Model::MAX_INFLUENCES; } } m_change = false; } } } void ContextInfluences::jointChanged( int index, int oldJoint, int newJoint ) { if ( !m_change ) { m_update = true; if ( newJoint >= 0 ) { for ( int i = 0; i < Model::MAX_INFLUENCES; i++ ) { if ( m_joints[i] == newJoint ) { // trying to assign new joint when we already have that joint // in one of the edit boxes. Change the selection back. This // will cause some nasty bugs and probably confuse the user. // Change the selection back. m_change = true; switch ( index ) { case 0: m_joint1->setCurrentIndex( oldJoint + 1 ); break; case 1: m_joint2->setCurrentIndex( oldJoint + 1 ); break; case 2: m_joint3->setCurrentIndex( oldJoint + 1 ); break; case 3: m_joint4->setCurrentIndex( oldJoint + 1 ); break; default: break; } m_change = false; m_update = false; return; } } } std::list l; m_model->getSelectedPositions( l ); std::list::iterator it; if ( oldJoint >= 0 ) { m_jclist[ oldJoint ].count = 0; for ( it = l.begin(); it != l.end(); it++ ) { m_model->removePositionInfluence( *it, oldJoint ); } } int weight = 0; if ( newJoint >= 0 ) { m_jclist[ newJoint ].count = l.size(); for ( it = l.begin(); it != l.end(); it++ ) { double w = m_model->calculatePositionInfluenceWeight( *it, newJoint ); log_debug( "influence = %f\n", w ); m_model->addPositionInfluence( *it, newJoint, Model::IT_Auto, w ); weight += (int) lround( w * 100.0 ); } if ( !l.empty() ) { m_jclist[ newJoint ].weight = (int) lround( weight / (double) l.size() ); } m_jclist[ newJoint ].typeIndex = Model::IT_Auto + 1; } m_joints[ index ] = newJoint; bool enabled = false; if ( newJoint >= 0 ) { enabled = true; m_change = true; updateWeightField( index, true, m_jclist[ newJoint ].typeIndex, m_jclist[ newJoint ].weight ); updateRemainders(); m_change = false; } else { updateWeightField( index, false, 0, 0 ); updateRemainders(); } m_model->operationComplete( tr( "Change Joint Assignment", "operation complete" ).toUtf8() ); emit panelChange(); m_update = false; } } void ContextInfluences::weightChanged( int index, double weight ) { if ( !m_change ) { m_update = true; if ( weight < 0.0 ) { weight = 0.0; } if ( weight > 1.0 ) { weight = 1.0; } int joint = m_joints[ index ]; log_debug( "setting joint %d weight to %f\n", joint, weight ); std::list l; m_model->getSelectedPositions( l ); std::list::iterator it; for ( it = l.begin(); it != l.end(); it++ ) { m_model->setPositionInfluenceType( *it, joint, Model::IT_Custom ); m_model->setPositionInfluenceWeight( *it, joint, weight ); } m_model->operationComplete( tr( "Change Influence Weight", "operation complete" ).toUtf8() ); updateRemainders(); emit panelChange(); m_update = false; } } void ContextInfluences::typeChanged( int index ) { if ( !m_change ) { m_update = true; int joint = m_joints[ index ]; QComboBox * typeValue = m_weight1; switch ( index ) { case 1: typeValue = m_weight2; break; case 2: typeValue = m_weight3; break; case 3: typeValue = m_weight4; break; default: break; } Model::InfluenceTypeE type = Model::IT_Auto; switch ( typeValue->currentIndex() ) { case 0: // Not really a valid selection, return m_update = false; return; case 1: type = Model::IT_Custom; break; case 2: type = Model::IT_Auto; break; case 3: type = Model::IT_Remainder; break; default: break; } log_debug( "setting joint %d type to %d\n", joint, (int) type ); std::list l; m_model->getSelectedPositions( l ); int weight = 0; std::list::iterator it; for ( it = l.begin(); it != l.end(); it++ ) { m_model->setPositionInfluenceType( *it, joint, type ); if ( type == Model::IT_Auto ) { double w = m_model->calculatePositionInfluenceWeight( *it, joint ); m_model->setPositionInfluenceWeight( *it, joint, w ); weight += (int) lround( w * 100.0 ); } else { m_model->setPositionInfluenceWeight( *it, joint, (double) m_jclist[ joint ].weight / 100.0 ); } } if ( type == Model::IT_Auto ) { m_jclist[ joint ].weight = (int) lround( weight / (double) m_jclist[ joint ].count ); } m_jclist[ joint ].typeIndex = type + 1; m_change = true; updateWeightField( index, true, m_jclist[ joint ].typeIndex, m_jclist[ joint ].weight ); updateRemainders(); m_change = false; m_model->operationComplete( tr( "Change Influence Type", "operation complete" ).toUtf8() ); emit panelChange(); m_update = false; } } void ContextInfluences::joint1Changed() { int index = 0; jointChanged( index, m_joints[ index ], m_joint1->currentIndex() - 1); } void ContextInfluences::joint2Changed() { int index = 1; jointChanged( index, m_joints[ index ], m_joint2->currentIndex() - 1); } void ContextInfluences::joint3Changed() { int index = 2; jointChanged( index, m_joints[ index ], m_joint3->currentIndex() - 1); } void ContextInfluences::joint4Changed() { int index = 3; jointChanged( index, m_joints[ index ], m_joint4->currentIndex() - 1); } void ContextInfluences::type1Changed() { int index = 0; typeChanged( index ); } void ContextInfluences::type2Changed() { int index = 1; typeChanged( index ); } void ContextInfluences::type3Changed() { int index = 2; typeChanged( index ); } void ContextInfluences::type4Changed() { int index = 3; typeChanged( index ); } void ContextInfluences::weight1Changed( const QString & weight ) { int index = 0; if ( weight.size() != 0 && weight[0].isDigit() ) { weightChanged( index, weight.toDouble() / 100.0 ); } } void ContextInfluences::weight2Changed( const QString & weight ) { int index = 1; if ( weight.size() != 0 && weight[0].isDigit() ) { weightChanged( index, weight.toDouble() / 100.0 ); } } void ContextInfluences::weight3Changed( const QString & weight ) { int index = 2; if ( weight.size() != 0 && weight[0].isDigit() ) { weightChanged( index, weight.toDouble() / 100.0 ); } } void ContextInfluences::weight4Changed( const QString & weight ) { int index = 3; if ( weight.size() != 0 && weight[0].isDigit() ) { weightChanged( index, weight.toDouble() / 100.0 ); } } void ContextInfluences::updateRemainders() { for ( int index = 0; index < Model::MAX_INFLUENCES; index++ ) { QComboBox * weightBox = m_weight1; switch ( index ) { case 0: weightBox = m_weight1; break; case 1: weightBox = m_weight2; break; case 2: weightBox = m_weight3; break; case 3: weightBox = m_weight4; break; default: break; } if ( weightBox->currentIndex() - 1 == Model::IT_Remainder ) { int w = getRemainderWeight( m_joints[ index ] ); log_debug( "updating box %d with remaining weight %d\n", index, w ); updateWeightField( index, weightBox->isEnabled(), weightBox->currentIndex(), w ); } } } void ContextInfluences::updateWeightField( int index, bool enabled, int type, int weight ) { QComboBox * weightBox = m_weight1; switch ( index ) { case 0: weightBox = m_weight1; break; case 1: weightBox = m_weight2; break; case 2: weightBox = m_weight3; break; case 3: weightBox = m_weight4; break; default: break; } weightBox->setItemText( 0, tr("", "multiple types of bone joint influence") ); weightBox->setItemText( 1, tr("Custom", "bone joint influence") ); weightBox->setItemText( 2, tr("Auto", "bone joint influence") ); weightBox->setItemText( 3, tr("Remainder", "bone joint influence") ); if ( enabled ) { QString typeStr; switch ( type ) { case 0: typeStr = tr("", "multiple types of bone joint influence"); break; case 1: typeStr = QString::asprintf( "%d", weight ); break; case 2: typeStr = tr("Auto: %1").arg( weight); break; case 3: typeStr = tr("Rem: %1").arg(weight); break; default: break; } weightBox->setItemText( type, typeStr ); weightBox->setCurrentIndex( type ); weightBox->setEnabled( true ); } else { weightBox->setCurrentIndex( 0 ); weightBox->setEnabled( false ); } // Update remainder fields switch ( index ) { case 0: weightBox = m_weight1; break; case 1: weightBox = m_weight2; break; case 2: weightBox = m_weight3; break; case 3: weightBox = m_weight4; break; default: break; } } int ContextInfluences::getRemainderWeight( int joint ) { log_debug( "getting remainder weight for joint %d\n", joint ); if ( joint >= 0 && joint < (int) m_model->getBoneJointCount() ) { list< Model::Position > plist; list< Model::Position >::iterator pit; Model::InfluenceList ilist; Model::InfluenceList::iterator iit; m_model->getSelectedPositions( plist ); double weight = 0.0; int count = 0; for ( pit = plist.begin(); pit != plist.end(); pit++ ) { m_model->getPositionInfluences( (*pit), ilist ); for ( iit = ilist.begin(); iit != ilist.end(); iit++ ) { if ( (*iit).m_type == Model::IT_Remainder && (*iit).m_boneId == joint ) { weight += (int) lround( (*iit).m_weight * 100.0 ); count++; } } } if ( count > 0 ) { return (int) lround( weight / (double) count ); } } return 0; } mm3d-1.3.15/src/implui/contextinfluences.h000066400000000000000000000051401466047437300204560ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTINFLUENCES_H #define __CONTEXTINFLUENCES_H #include "contextinfluences.base.h" #include #include "contextwidget.h" #include "model.h" class ContextInfluences : public QWidget, public Ui::ContextInfluencesBase, public ContextWidget { Q_OBJECT public: ContextInfluences( QWidget * parent ); virtual ~ContextInfluences(); // ContextWidget methods void setModel( Model * ); void modelChanged( int changeBits ); bool isUpdating() { return m_update; }; signals: void panelChange(); public slots: // Influence slots void joint1Changed(); void joint2Changed(); void joint3Changed(); void joint4Changed(); void type1Changed(); void type2Changed(); void type3Changed(); void type4Changed(); void weight1Changed( const QString & ); void weight2Changed( const QString & ); void weight3Changed( const QString & ); void weight4Changed( const QString & ); protected: class JointCount { public: JointCount(); bool inList; int count; int typeCount[ Model::IT_MAX ]; int typeIndex; int weight; }; typedef std::vector< JointCount > JointCountList; void jointChanged( int index, int oldJoint, int newJoint ); void weightChanged( int index, double weight ); void typeChanged( int index ); void updateRemainders(); void updateWeightField( int index, bool enabled, int type, int weight ); int getRemainderWeight( int joint ); Model * m_model; bool m_change; bool m_update; int m_joints[4]; JointCountList m_jclist; }; #endif // __CONTEXTINFLUENCES_H mm3d-1.3.15/src/implui/contextname.cc000066400000000000000000000053351466047437300174070ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextname.h" #include "model.h" #include #include ContextName::ContextName( QWidget * parent ) : QWidget( parent ), m_change( false ), m_update( false ) { setupUi( this ); } ContextName::~ContextName() { } void ContextName::setModel( Model * m ) { m_model = m; modelChanged( ~0 ); } void ContextName::modelChanged( int changeBits ) { if ( !m_update ) { m_change = true; unsigned bcount = m_model->getBoneJointCount(); for ( unsigned b = 0; b < bcount; b++ ) { if ( m_model->isBoneJointSelected( b ) ) { m_name->setText( QString::fromUtf8( m_model->getBoneJointName( b ) ) ); break; } } unsigned pcount = m_model->getPointCount(); for ( unsigned p = 0; p < pcount; p++ ) { if ( m_model->isPointSelected( p ) ) { m_name->setText( QString::fromUtf8( m_model->getPointName( p ) ) ); break; } } m_change = false; } } void ContextName::textChangedEvent( const QString & nameStr ) { if ( !m_change ) { m_update = true; // Change model based on text field input unsigned bcount = m_model->getBoneJointCount(); for ( unsigned b = 0; b < bcount; b++ ) { if ( m_model->isBoneJointSelected( b ) ) { m_model->setBoneJointName( b, (const char*) nameStr.toUtf8() ); break; } } unsigned pcount = m_model->getPointCount(); for ( unsigned p = 0; p < pcount; p++ ) { if ( m_model->isPointSelected( p ) ) { m_model->setPointName( p, (const char*) nameStr.toUtf8() ); break; } } m_model->operationComplete( tr( "Rename", "operation complete" ).toUtf8() ); emit panelChange(); m_update = false; } } mm3d-1.3.15/src/implui/contextname.h000066400000000000000000000030661466047437300172500ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTNAME_H #define __CONTEXTNAME_H #include "contextname.base.h" #include "contextwidget.h" #include class Model; class ContextName : public QWidget, public Ui::ContextNameBase, public ContextWidget { Q_OBJECT public: ContextName( QWidget * parent ); virtual ~ContextName(); // ContextWidget methods void setModel( Model * ); void modelChanged( int changeBits ); bool isUpdating() { return m_update; }; signals: void panelChange(); public slots: // Position slots void textChangedEvent( const QString & ); protected: Model * m_model; bool m_change; bool m_update; }; #endif // __CONTEXTNAME_H mm3d-1.3.15/src/implui/contextpanel.cc000066400000000000000000000173701466047437300175700ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextpanel.h" #include "viewpanel.h" #include "contextname.h" #include "contextposition.h" #include "contextrotation.h" #include "contextgroup.h" #include "contextinfluences.h" #include "contextprojection.h" #include "log.h" #include #include #include #include ContextPanel::ContextPanel( QMainWindow * parent, ViewPanel * panel, ContextPanelObserver * ob ) : QDockWidget( tr( "Properties", "Window title" ), parent ), m_model( NULL ), m_observer( ob ), m_panel( panel ), m_mainWidget( new QWidget( parent ) ), m_spacer( NULL ) { setWidget( m_mainWidget ); m_layout = new QBoxLayout( QBoxLayout::TopToBottom, m_mainWidget ); } ContextPanel::~ContextPanel() { } void ContextPanel::setModel( Model * model ) { ContextWidgetList::iterator it; for ( it = m_widgets.begin(); it!= m_widgets.end(); it++ ) { delete *it; } m_widgets.clear(); if ( m_spacer ) { delete m_spacer; m_spacer = NULL; } if ( m_model != model ) { model->addObserver( this ); } m_model = model; modelChanged( Model::ChangeAll ); } void ContextPanel::modelChanged( int changeBits ) { if ( this->isVisible() ) { log_debug( "modelChanged()\n" ); setUpdatesEnabled( false ); ContextWidgetList::iterator it; // If the panel caused the change, update all // panels and return for ( it = m_widgets.begin(); it!= m_widgets.end(); it++ ) { if ( (*it)->isUpdating() ) { for ( it = m_widgets.begin(); it!= m_widgets.end(); it++ ) { (*it)->modelChanged( changeBits ); } setUpdatesEnabled( true ); return; } } // If selection didn't change, just send an update if ( !(changeBits & (Model::SelectionChange | Model::AnimationMode)) ) { for ( it = m_widgets.begin(); it!= m_widgets.end(); it++ ) { (*it)->modelChanged( changeBits ); } setUpdatesEnabled( true ); return; } log_debug( "deleting and re-creating (%08X)\n", changeBits ); // Otherwise, delete panels and recreate // TODO: may optimize by not deleting and re-creating, need to // be smarter about changes for ( it = m_widgets.begin(); it!= m_widgets.end(); it++ ) { delete *it; } m_widgets.clear(); { if ( Model::ANIMMODE_NONE == m_model->getAnimationMode() && (m_model->getSelectedBoneJointCount() + m_model->getSelectedPointCount()) == 1 ) { ContextName * name = new ContextName( m_mainWidget ); name->setModel( m_model ); m_layout->addWidget( name ); name->show(); connect( name, SIGNAL(panelChange()), m_panel, SLOT(modelUpdatedEvent())); m_widgets.push_back( name ); } } // Position should always be visible ContextPosition * pos = new ContextPosition( m_mainWidget ); pos->setModel( m_model ); m_layout->addWidget( pos ); pos->show(); connect( pos, SIGNAL(panelChange()), m_panel, SLOT(modelUpdatedEvent())); m_widgets.push_back( pos ); { // Only allow points in None and Frame // Only allow joints in None and Skel int pcount = m_model->getSelectedPointCount(); int jcount = m_model->getSelectedBoneJointCount(); if ( ((pcount == 1) && (jcount == 0) && m_model->getAnimationMode() != Model::ANIMMODE_SKELETAL ) || ((jcount == 1) && (pcount == 0) && m_model->getAnimationMode() != Model::ANIMMODE_FRAME ) ) { ContextRotation * rot = new ContextRotation( m_mainWidget ); rot->setModel( m_model ); m_layout->addWidget( rot ); rot->show(); connect( rot, SIGNAL(panelChange()), m_panel, SLOT(modelUpdatedEvent())); m_widgets.push_back( rot ); } } unsigned int tcount = m_model->getTriangleCount(); for ( unsigned int t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { ContextGroup * grp = new ContextGroup( m_mainWidget, m_observer ); grp->setModel( m_model ); m_layout->addWidget( grp ); grp->show(); connect( grp, SIGNAL(panelChange()), m_panel, SLOT(modelUpdatedEvent())); m_widgets.push_back( grp ); break; } } unsigned int pcount = m_model->getProjectionCount(); for ( unsigned int p = 0; p < pcount; p++ ) { if ( m_model->isProjectionSelected( p ) ) { ContextProjection * prj = new ContextProjection( m_mainWidget, m_observer ); prj->setModel( m_model ); m_layout->addWidget( prj ); prj->show(); connect( prj, SIGNAL(panelChange()), m_panel, SLOT(modelUpdatedEvent())); m_widgets.push_back( prj ); break; } } // Only show influences if there are influences to show if ( m_model->getBoneJointCount() > 0 && m_model->getAnimationMode() == Model::ANIMMODE_NONE ) { bool showInfluences = false; unsigned pointCount = m_model->getPointCount(); for ( unsigned n = 0; !showInfluences && n < pointCount; n++ ) { if ( m_model->isPointSelected( n ) ) { showInfluences = true; } } unsigned vcount = m_model->getVertexCount(); for ( unsigned v = 0; !showInfluences && v < vcount; v++ ) { if ( m_model->isVertexSelected( v ) ) { showInfluences = true; } } if ( showInfluences ) { ContextInfluences * prj = new ContextInfluences( m_mainWidget ); prj->setModel( m_model ); m_layout->addWidget( prj ); prj->show(); connect( prj, SIGNAL(panelChange()), m_panel, SLOT(modelUpdatedEvent())); m_widgets.push_back( prj ); } } m_spacer = new QSpacerItem( 0, 0, QSizePolicy::Preferred, QSizePolicy::MinimumExpanding ); setUpdatesEnabled( true ); } } void ContextPanel::show() { QDockWidget::show(); // this is causing a segfault on dock/undock because the widgets // are being destroyed and re-created. The solution is to call setModel // explicitly whenever the window is shown (this only happens in viewwin.cc) //setModel( m_model ) } void ContextPanel::close() { hide(); // Do hide instead } void ContextPanel::hide() { log_debug( "ContextPanel::hide()\n" ); QDockWidget::hide(); } void ContextPanel::contextMenuEvent( QContextMenuEvent * e ) { e->ignore(); } void ContextPanel::closeEvent( QCloseEvent * e ) { emit panelHidden(); } mm3d-1.3.15/src/implui/contextpanel.h000066400000000000000000000037351466047437300174320ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTPANEL_H #define __CONTEXTPANEL_H #include "contextpanelobserver.h" #include "contextwidget.h" #include "model.h" #include #include class QContextMenuEvent; class QCloseEvent; class QBoxLayout; class QSpacerItem; class ViewPanel; class ContextPanelObserver; class ContextPanel : public QDockWidget, public Model::Observer { Q_OBJECT public: ContextPanel( QMainWindow * parent, ViewPanel * panel, ContextPanelObserver * ob ); virtual ~ContextPanel(); // QDockWidget methods public slots: void show(); void close(); void hide(); void closeEvent( QCloseEvent * e ); void setModel( Model * m ); // Model::Observer methods void modelChanged( int changeBits ); signals: void panelHidden(); protected: void contextMenuEvent( QContextMenuEvent * e ); Model * m_model; ContextPanelObserver * m_observer; ViewPanel * m_panel; QWidget * m_mainWidget; QBoxLayout * m_layout; QSpacerItem * m_spacer; ContextWidgetList m_widgets; }; #endif // __CONTEXTPANEL_H mm3d-1.3.15/src/implui/contextposition.cc000066400000000000000000000106571466047437300203360ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextposition.h" #include "model.h" #include #include ContextPosition::ContextPosition( QWidget * parent ) : QWidget( parent ), m_change( false ), m_update( false ) { setupUi( this ); } ContextPosition::~ContextPosition() { } void ContextPosition::setModel( Model * m ) { m_model = m; modelChanged( ~0 ); } void ContextPosition::modelChanged( int changeBits ) { if ( !m_update ) { m_change = true; // Update coordinates in text fields bool first = true; double cmin[3]; double cmax[3]; double coords[3]; int i; for ( i = 0; i < 3; i++ ) { cmin[i] = 0.0; cmax[i] = 0.0; } /* unsigned vcount = m_model->getVertexCount(); for ( unsigned v = 0; v < vcount; v++ ) { if ( m_model->isVertexSelected( v ) ) { m_model->getVertexCoords( v, coords ); if ( !first ) { for ( i = 0; i < 3; i++ ) { if ( coords[i] < cmin[i] ) cmin[i] = coords[i]; if ( coords[i] > cmax[i] ) cmax[i] = coords[i]; } } else { first = false; for ( i = 0; i < 3; i++ ) { cmin[i] = coords[i]; cmax[i] = coords[i]; } } } } unsigned pcount = m_model->getPointCount(); */ std::list posList; m_model->getSelectedPositions( posList ); std::list::iterator it; for ( it = posList.begin(); it != posList.end(); it++ ) { m_model->getPositionCoords( *it, coords ); if ( !first ) { for ( i = 0; i < 3; i++ ) { if ( coords[i] < cmin[i] ) cmin[i] = coords[i]; if ( coords[i] > cmax[i] ) cmax[i] = coords[i]; } } else { first = false; for ( i = 0; i < 3; i++ ) { cmin[i] = coords[i]; cmax[i] = coords[i]; } } } for ( i = 0; i < 3; i++ ) { m_coords[i] = (cmin[i] + cmax[i]) / 2.0; } QString str; str = QString::asprintf( "%f", m_coords[0] ); m_xValue->setText( str ); str = QString::asprintf( "%f", m_coords[1] ); m_yValue->setText( str ); str = QString::asprintf( "%f", m_coords[2] ); m_zValue->setText( str ); str = QString::asprintf( "%g, %g, %g", cmax[0]-cmin[0], cmax[1]-cmin[1], cmax[2]-cmin[2] ); m_dimensionsValue->setText( str ); m_change = false; } } void ContextPosition::updatePosition() { if ( !m_change ) { m_update = true; // Change model based on text field input double coords[3]; double trans[3]; coords[0] = m_xValue->text().toDouble(); coords[1] = m_yValue->text().toDouble(); coords[2] = m_zValue->text().toDouble(); trans[0] = coords[0] - m_coords[0]; trans[1] = coords[1] - m_coords[1]; trans[2] = coords[2] - m_coords[2]; m_coords[0] = coords[0]; m_coords[1] = coords[1]; m_coords[2] = coords[2]; Matrix m; m.setTranslation( trans[0], trans[1], trans[2] ); m_model->translateSelected( m ); m_model->operationComplete( tr( "Set Position", "operation complete" ).toUtf8() ); emit panelChange(); m_update = false; } } mm3d-1.3.15/src/implui/contextposition.h000066400000000000000000000031361466047437300201720ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTPOSITION_H #define __CONTEXTPOSITION_H #include "contextposition.base.h" #include #include "contextwidget.h" class Model; class ContextPosition : public QWidget, public Ui::ContextPositionBase, public ContextWidget { Q_OBJECT public: ContextPosition( QWidget * parent ); virtual ~ContextPosition(); // ContextWidget methods void setModel( Model * ); void modelChanged( int changeBits ); bool isUpdating() { return m_update; }; signals: void panelChange(); public slots: // Position slots void updatePosition(); protected: Model * m_model; double m_coords[3]; bool m_change; bool m_update; }; #endif // __CONTEXTPOSITION_H mm3d-1.3.15/src/implui/contextprojection.cc000066400000000000000000000053061466047437300206410ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextprojection.h" #include "model.h" #include "contextpanelobserver.h" #include "groupwin.h" #include "texwin.h" #include #include #include #include ContextProjection::ContextProjection( QWidget * parent, ContextPanelObserver * ob ) : QWidget( parent ), m_model( NULL ), m_observer( ob ), m_change( false ), m_update( false ) { setupUi( this ); } ContextProjection::~ContextProjection() { } void ContextProjection::setModel( Model * m ) { m_model = m; modelChanged( ~0 ); } void ContextProjection::modelChanged( int changeBits ) { // Only change if it's a group change or a selection change if ( (changeBits & Model::AddOther) || (changeBits & Model::SelectionChange ) ) { if ( !m_update ) { m_change = true; // Update projection fields unsigned int pcount = m_model->getProjectionCount(); for ( unsigned int p = 0; p < pcount; p++ ) { if ( m_model->isProjectionSelected( p ) ) { m_typeValue->setCurrentIndex( m_model->getProjectionType( p ) ); break; } } m_change = false; } } } void ContextProjection::typeChanged() { if ( !m_change ) { m_update = true; int type = m_typeValue->currentIndex(); unsigned pcount = m_model->getProjectionCount(); for ( unsigned p = 0; p < pcount; p++ ) { if ( m_model->isProjectionSelected( p ) ) { m_model->setProjectionType( p, type ); } } m_model->operationComplete( tr( "Set Projection Type", "operation complete" ).toUtf8() ); emit panelChange(); m_update = false; } } void ContextProjection::projectionPropertiesClicked() { m_observer->showProjectionEvent(); } mm3d-1.3.15/src/implui/contextprojection.h000066400000000000000000000033351466047437300205030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTPROJECTION_H #define __CONTEXTPROJECTION_H #include "contextprojection.base.h" #include #include "contextwidget.h" class ContextPanelObserver; class Model; class ContextProjection : public QWidget, public Ui::ContextProjectionBase, public ContextWidget { Q_OBJECT public: ContextProjection( QWidget * parent, ContextPanelObserver * ob ); virtual ~ContextProjection(); // ContextWidget methods void setModel( Model * ); void modelChanged( int changeBits ); bool isUpdating() { return m_update; }; signals: void panelChange(); public slots: // Projection slots void typeChanged(); void projectionPropertiesClicked(); protected: Model * m_model; ContextPanelObserver * m_observer; bool m_change; bool m_update; }; #endif // __CONTEXTPROJECTION_H mm3d-1.3.15/src/implui/contextrotation.cc000066400000000000000000000122461466047437300203250ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "contextrotation.h" #include "model.h" #include #include ContextRotation::ContextRotation( QWidget * parent ) : QWidget( parent ), m_change( false ), m_update( false ) { setupUi( this ); } ContextRotation::~ContextRotation() { } void ContextRotation::setModel( Model * m ) { m_model = m; modelChanged( ~0 ); } void ContextRotation::modelChanged( int changeBits ) { if ( !m_update ) { m_change = true; bool searching = true; // Update coordinates in text fields double rad[3]; memset( rad, 0, sizeof(rad) ); unsigned int pcount = m_model->getPointCount(); for ( unsigned int p = 0; searching && p < pcount; p++ ) { if ( m_model->isPointSelected( p ) ) { searching = false; m_model->getPointRotation( p, rad ); } } Model::AnimationModeE animationMode = m_model->getAnimationMode(); if ( animationMode == Model::ANIMMODE_NONE || animationMode == Model::ANIMMODE_SKELETAL ) { unsigned int bcount = m_model->getBoneJointCount(); for ( unsigned int b = 0; searching && b < bcount; b++ ) { if ( m_model->isBoneJointSelected(b) ) { if ( animationMode == Model::ANIMMODE_SKELETAL ) { int anim = m_model->getCurrentAnimation(); int frame = m_model->getCurrentAnimationFrame(); if ( m_model->getSkelAnimKeyframe( anim, frame, b, true, rad[0], rad[1], rad[2] ) ) { searching = false; } } else { searching = false; Matrix rm; m_model->getBoneJointRelativeMatrix( b, rm ); rm.getRotation( rad[0], rad[1], rad[2] ); } } } } for ( int i = 0; i < 3; i++ ) { rad[i] = rad[i] / PIOVER180; } QString str; str = QString::asprintf( "%f", (float)rad[0] ); m_xValue->setText( str ); str = QString::asprintf( "%f", (float)rad[1] ); m_yValue->setText( str ); str = QString::asprintf( "%f", (float)rad[2] ); m_zValue->setText( str ); this->setEnabled( searching ? false : true ); m_change = false; } } void ContextRotation::updateRotation() { if ( !m_change ) { m_update = true; // Change model based on text field input double rad[3]; rad[0] = m_xValue->text().toDouble(); rad[1] = m_yValue->text().toDouble(); rad[2] = m_zValue->text().toDouble(); rad[0] = rad[0] * PIOVER180; rad[1] = rad[1] * PIOVER180; rad[2] = rad[2] * PIOVER180; bool searching = true; unsigned int pcount = m_model->getPointCount(); for ( unsigned int p = 0; searching && p < pcount; p++ ) { if ( m_model->isPointSelected( p ) ) { searching = false; m_model->setPointRotation( p, rad ); } } Model::AnimationModeE animationMode = m_model->getAnimationMode(); if ( animationMode == Model::ANIMMODE_NONE || animationMode == Model::ANIMMODE_SKELETAL ) { unsigned int bcount = m_model->getBoneJointCount(); for ( unsigned int b = 0; searching && b < bcount; b++ ) { if ( m_model->isBoneJointSelected(b) ) { if ( animationMode == Model::ANIMMODE_SKELETAL ) { int anim = m_model->getCurrentAnimation(); int frame = m_model->getCurrentAnimationFrame(); searching = false; m_model->setSkelAnimKeyframe( anim, frame, b, true, rad[0], rad[1], rad[2] ); m_model->setCurrentAnimationFrame( frame ); // Force re-animate } else { searching = false; m_model->setBoneJointRotation( b, rad ); m_model->setupJoints(); } } } } if ( !searching ) { m_model->operationComplete( tr( "Set Rotation", "operation complete" ).toUtf8() ); emit panelChange(); } m_update = false; } } mm3d-1.3.15/src/implui/contextrotation.h000066400000000000000000000031031466047437300201570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CONTEXTROTATION_H #define __CONTEXTROTATION_H #include "contextrotation.base.h" #include #include "contextwidget.h" class Model; class ContextRotation : public QWidget, public Ui::ContextRotationBase, public ContextWidget { Q_OBJECT public: ContextRotation( QWidget * parent ); virtual ~ContextRotation(); // ContextWidget methods void setModel( Model * ); void modelChanged( int changeBits ); bool isUpdating() { return m_update; }; signals: void panelChange(); public slots: // Rotation slots void updateRotation(); protected: Model * m_model; bool m_change; bool m_update; }; #endif // __CONTEXTROTATION_H mm3d-1.3.15/src/implui/extrudewin.cc000066400000000000000000000231361466047437300172570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "extrudewin.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "decalmgr.h" #include "3dmprefs.h" #include "helpwin.h" #include #include #include using std::list; using std::map; ExtrudeWin::ExtrudeWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setupUi( this ); setModal( true ); if ( g_prefs.exists( "ui_extrude_makebackfaces" ) ) { int val = g_prefs( "ui_extrude_makebackfaces" ).intValue(); if ( val != 0 ) { m_backFaceCheckbox->setChecked( true ); } else { m_backFaceCheckbox->setChecked( false ); } } QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } ExtrudeWin::~ExtrudeWin() { } void ExtrudeWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_commands.html#extrude", true ); win->show(); } void ExtrudeWin::absoluteExtrudeEvent() { m_sides.clear(); m_evMap.clear(); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Extrude complete").toUtf8().data() ); // get extrude arguments double x = m_xEdit->text().toDouble(); double y = m_yEdit->text().toDouble(); double z = m_zEdit->text().toDouble(); list faces; m_model->getSelectedTriangles( faces ); list vertices; m_model->getSelectedVertices( vertices ); log_debug( "extruding %" PORTuSIZE " faces on %f,%f,%f\n", faces.size(), x, y, z ); // Find edges (sides with count==1) list::iterator it; for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = m_model->getTriangleVertex( *it, t ); } for ( int t = 0; t < (3 - 1); t++ ) { addSide( v[t], v[t+1] ); } addSide( v[0], v[2] ); } // make extruded vertices and create a map from old vertices // to new vertices for ( it = vertices.begin(); it != vertices.end(); it++ ) { double coord[3]; m_model->getVertexCoords( *it, coord ); coord[0] += x; coord[1] += y; coord[2] += z; unsigned i = m_model->addVertex( coord[0], coord[1], coord[2] ); m_evMap[*it] = i; log_debug( "added vertex %d for %d at %f,%f,%f\n", m_evMap[*it], *it, coord[0], coord[1], coord[2] ); } // Add faces for edges for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = m_model->getTriangleVertex( *it, t ); } for ( int t = 0; t < (3 - 1); t++ ) { if ( sideIsEdge( v[t], v[t+1] ) ) { makeFaces( v[t], v[t+1] ); } } if ( sideIsEdge( v[2], v[0] ) ) { makeFaces( v[2], v[0] ); } } // Map selected faces onto extruded vertices for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned tri = *it; int v1 = m_model->getTriangleVertex( tri, 0 ); int v2 = m_model->getTriangleVertex( tri, 1 ); int v3 = m_model->getTriangleVertex( tri, 2 ); if ( m_backFaceCheckbox->isChecked() ) { int newTri = m_model->addTriangle( v1, v2, v3 ); m_model->invertNormals( newTri ); } log_debug( "face %d uses vertices %d,%d,%d\n", *it, v1, v2, v3 ); m_model->setTriangleVertices( tri, m_evMap[ m_model->getTriangleVertex( tri, 0 ) ], m_evMap[ m_model->getTriangleVertex( tri, 1 ) ], m_evMap[ m_model->getTriangleVertex( tri, 2 ) ] ); log_debug( "moved face %d to vertices %d,%d,%d\n", *it, m_model->getTriangleVertex( tri, 0 ), m_model->getTriangleVertex( tri, 1 ), m_model->getTriangleVertex( tri, 2 ) ); } // Update face selection ExtrudedVertexMap::iterator evit; for ( evit = m_evMap.begin(); evit != m_evMap.end(); evit++ ) { m_model->unselectVertex( (*evit).first ); m_model->selectVertex( (*evit).second ); } m_model->deleteOrphanedVertices(); m_model->operationComplete( tr( "Extrude", "operation complete" ).toUtf8() ); DecalManager::getInstance()->modelUpdated( m_model ); } void ExtrudeWin::normalExtrudeEvent() { /* m_sides.clear(); m_evMap.clear(); double magnitude = m_normalEdit->text().toDouble(); list faces = m_model->getSelectedTriangles(); list vertices = m_model->getSelectedVertices(); // Find edges (sides with count==1) list::iterator it; for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = m_model->getTriangleVertex( *it, t ); // Capture normal while we're here // Note that if normals aren't smoothed the extrude may not // have the intended effect VertexNormal vn; m_model->getNormal( *it, t, vn.val ); m_vnMap[ v[t] ] = vn; log_debug( "vertex %d normals: %f,%f,%f\n", v[t], vn.val[0], vn.val[1], vn.val[2] ); } for ( int t = 0; t < (3 - 1); t++ ) { addSide( v[t], v[t+1] ); } addSide( v[0], v[2] ); } // make extruded vertices and create a map from old vertices // to new vertices for ( it = vertices.begin(); it != vertices.end(); it++ ) { double coord[3]; m_model->getVertexCoords( *it, coord ); VertexNormal vn = m_vnMap[ *it ]; coord[0] += vn.val[0] * magnitude; coord[1] += vn.val[1] * magnitude; coord[2] += vn.val[2] * magnitude; unsigned i = m_model->addVertex( coord[0], coord[1], coord[2] ); m_evMap[*it] = i; log_debug( "added vertex %d for %d at %f,%f,%f\n", m_evMap[*it], *it, coord[0], coord[1], coord[2] ); } // Add faces for edges for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = m_model->getTriangleVertex( *it, t ); } for ( int t = 0; t < (3 - 1); t++ ) { if ( sideIsEdge( v[t], v[t+1] ) ) { makeFaces( v[t], v[t+1] ); } } if ( sideIsEdge( v[2], v[0] ) ) { makeFaces( v[2], v[0] ); } } // Map selected faces onto extruded vertices for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned tri = *it; log_debug( "face %d uses vertices %d,%d,%d\n", *it, m_model->getTriangleVertex( tri, 0 ), m_model->getTriangleVertex( tri, 1 ), m_model->getTriangleVertex( tri, 2 ) ); m_model->setTriangleVertices( tri, m_evMap[ m_model->getTriangleVertex( tri, 0 ) ], m_evMap[ m_model->getTriangleVertex( tri, 1 ) ], m_evMap[ m_model->getTriangleVertex( tri, 2 ) ] ); log_debug( "moved face %d to vertices %d,%d,%d\n", *it, m_model->getTriangleVertex( tri, 0 ), m_model->getTriangleVertex( tri, 1 ), m_model->getTriangleVertex( tri, 2 ) ); } // Update face selection ExtrudedVertexMap::iterator evit; for ( evit = m_evMap.begin(); evit != m_evMap.end(); evit++ ) { m_model->unselectVertex( (*evit).first ); m_model->selectVertex( (*evit).second ); } m_model->operationComplete(); DecalManager::getInstance()->modelUpdated( m_model ); */ } void ExtrudeWin::backFacesChanged( bool o ) { g_prefs( "ui_extrude_makebackfaces" ) = (o ? 1 : 0); } void ExtrudeWin::makeFaces( unsigned a, unsigned b ) { unsigned a2 = m_evMap[a]; unsigned b2 = m_evMap[b]; m_model->addTriangle( b, b2, a2 ); m_model->addTriangle( a2, a, b ); } void ExtrudeWin::addSide( unsigned a, unsigned b ) { // Make sure a < b to simplify comparison below if ( b < a ) { unsigned c = a; a = b; b = c; } // Find existing side (if any) and increment count SideList::iterator it; for ( it = m_sides.begin(); it != m_sides.end(); it++ ) { if ( (*it).a == a && (*it).b == b ) { (*it).count++; log_debug( "side (%d,%d) = %d\n", a, b, (*it).count ); return; } } // Not found, add new side with a count of 1 SideT s; s.a = a; s.b = b; s.count = 1; log_debug( "side (%d,%d) = %d\n", a, b, s.count ); m_sides.push_back( s ); } bool ExtrudeWin::sideIsEdge( unsigned a, unsigned b ) { // Make sure a < b to simplify comparison below if ( b < a ) { unsigned c = a; a = b; b = c; } SideList::iterator it; for ( it = m_sides.begin(); it != m_sides.end(); it++ ) { if ( (*it).a == a && (*it).b == b ) { if ( (*it).count == 1 ) { return true; } else { return false; } } } return false; } mm3d-1.3.15/src/implui/extrudewin.h000066400000000000000000000037371466047437300171260ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __EXTRUDEWIN_H #define __EXTRUDEWIN_H #include "extrudewin.base.h" #include #include #include class Model; class ExtrudeWin : public QDialog, public Ui::ExtrudeWinBase { Q_OBJECT public: ExtrudeWin( Model *, QWidget * parent = NULL ); virtual ~ExtrudeWin(); public slots: void helpNowEvent(); void absoluteExtrudeEvent(); void normalExtrudeEvent(); void backFacesChanged( bool ); protected: struct _Side_t { unsigned a; unsigned b; int count; }; typedef struct _Side_t SideT; typedef struct { float val[3]; } VertexNormal; typedef std::list SideList; typedef std::map ExtrudedVertexMap; typedef std::map VertexNormalMap; void makeFaces( unsigned a, unsigned b ); void addSide( unsigned a, unsigned b ); bool sideIsEdge( unsigned a, unsigned b ); Model * m_model; SideList m_sides; ExtrudedVertexMap m_evMap; VertexNormalMap m_vnMap; }; #endif // __EXTRUDEWIN_H mm3d-1.3.15/src/implui/groupclean.cc000066400000000000000000000054671466047437300172270ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2009 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "groupclean.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include using std::list; using std::map; GroupCleanWin::GroupCleanWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setAttribute( Qt::WA_DeleteOnClose ); setModal( true ); setupUi( this ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } GroupCleanWin::~GroupCleanWin() { } void GroupCleanWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_groupclean.html", true ); win->show(); } void GroupCleanWin::accept() { int previousGroups = m_model->getGroupCount(); int previousMaterials = m_model->getTextureCount(); int mergedMaterials = 0; int removedMaterials = 0; int mergedGroups = 0; int removedGroups = 0; if ( m_mergeMaterials->isChecked() ) { mergedMaterials = m_model->mergeIdenticalMaterials(); } if ( m_removeMaterials->isChecked() ) { removedMaterials = m_model->removeUnusedMaterials(); } if ( m_mergeGroups->isChecked() ) { mergedGroups = m_model->mergeIdenticalGroups(); } if ( m_removeGroups->isChecked() ) { removedGroups = m_model->removeUnusedGroups(); } m_model->operationComplete( tr( "Group Clean-up", "operation complete" ).toUtf8() ); model_status( m_model, StatusNormal, STATUSTIME_LONG, "%s", (tr( "Merged %1 groups, %2 materials; Removed %3 of %4 groups, %5 of %6 materials" ) .arg(mergedGroups).arg(mergedMaterials).arg(removedGroups).arg(previousGroups).arg(removedMaterials).arg(previousMaterials)).toUtf8().data() ); QDialog::accept(); } void GroupCleanWin::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/groupclean.h000066400000000000000000000024371466047437300170630ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2009 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __GROUPCLEAN_H #define __GROUPCLEAN_H #include "groupclean.base.h" #include class Model; class GroupCleanWin : public QDialog, public Ui::GroupCleanBase { Q_OBJECT public: GroupCleanWin( Model *, QWidget * parent = NULL ); virtual ~GroupCleanWin(); public slots: void accept(); void reject(); void helpNowEvent(); protected: Model * m_model; }; #endif // __GROUPCLEAN_H mm3d-1.3.15/src/implui/groupwin.cc000066400000000000000000000203271466047437300167320ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "groupwin.h" #include "model.h" #include "textureframe.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include #include #include #include #include #include #include GroupWindow::GroupWindow( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); m_textureFrame->setModel( model ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); for ( int t = 0; t < m_model->getTextureCount(); t++ ) { m_textureComboBox->insertItem( t+1, QString::fromUtf8( m_model->getTextureName( t ) ) ); } for ( int t = 0; t < m_model->getGroupCount(); t++ ) { m_groupComboBox->insertItem( t+1, QString::fromUtf8( m_model->getGroupName( t ) ) ); } list triangles; m_model->getSelectedTriangles( triangles ); list::iterator it; for ( it = triangles.begin(); it != triangles.end(); it++ ) { int g = m_model->getTriangleGroup( *it ); if ( g >= 0 ) { m_groupComboBox->setCurrentIndex( g + 1 ); m_textureComboBox->setCurrentIndex( m_model->getGroupTextureId( g ) + 1 ); break; } } groupSelectedEvent( m_groupComboBox->currentIndex() ); updateTexture(); } GroupWindow::~GroupWindow() { } void GroupWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_groupwin.html", true ); win->show(); } void GroupWindow::newClickedEvent() { bool ok = true; bool valid = false; while ( !valid ) { QString groupName = QInputDialog::getText( this, tr("New group", "window title"), tr("Enter new group name:"), QLineEdit::Normal, QString(), &ok ); if ( ok == true ) { if ( groupName.length() > 0 && groupName.length() < Model::MAX_GROUP_NAME_LEN ) { int groupNum = m_model->addGroup( groupName.toUtf8() ); m_groupComboBox->insertItem( groupNum + 1, groupName ); m_groupComboBox->setCurrentIndex( groupNum + 1 ); groupSelectedEvent( groupNum + 1 ); valid = true; } else { QString msg = tr( "Group name must be between 1 and %1 characters" ).arg( Model::MAX_GROUP_NAME_LEN - 1 ); QMessageBox::warning( this, tr("Bad group name", "window title"), msg, QMessageBox::Ok | QMessageBox::Default, 0, 0 ); } } else { valid = true; } } } void GroupWindow::renameClickedEvent() { bool ok = true; bool valid = false; int groupNum = m_groupComboBox->currentIndex(); if ( groupNum == 0 ) { QMessageBox::information( this, tr("Cannot change", "cannot change group name, window title"), tr("You cannot change the default group name"), QMessageBox::Ok | QMessageBox::Default, 0, 0 ); return; } groupNum--; while ( !valid ) { QString groupName = QInputDialog::getText( this, tr("New group", "window title"), tr("Enter new group name:"), QLineEdit::Normal, QString::fromUtf8( m_model->getGroupName( groupNum ) ), &ok ); if ( ok == true ) { if ( groupName.length() > 0 && groupName.length() < Model::MAX_GROUP_NAME_LEN ) { m_model->setGroupName( groupNum, groupName.toUtf8() ); m_groupComboBox->setItemText( groupNum + 1, groupName ); valid = true; } else { QString msg = QString::asprintf( "Group name must be between 1 and %d characters", Model::MAX_GROUP_NAME_LEN - 1 ); QMessageBox::warning( this, tr("Bad group name", "window title"), msg, QMessageBox::Ok | QMessageBox::Default, 0, 0 ); } } else { valid = true; } } } void GroupWindow::deleteClickedEvent() { int groupNum = m_groupComboBox->currentIndex(); m_groupComboBox->removeItem( groupNum ); m_model->deleteGroup( groupNum - 1 ); m_groupComboBox->setCurrentIndex( 0 ); groupSelectedEvent( 0 ); } void GroupWindow::selectFacesClickedEvent() { m_model->unselectAll(); m_model->selectGroup( m_groupComboBox->currentIndex() - 1 ); DecalManager::getInstance()->modelUpdated( m_model ); } void GroupWindow::unselectFacesClickedEvent() { m_model->unselectGroup( m_groupComboBox->currentIndex() - 1 ); DecalManager::getInstance()->modelUpdated( m_model ); } void GroupWindow::assignAsGroupClickedEvent() { m_model->setSelectedAsGroup( m_groupComboBox->currentIndex() - 1 ); DecalManager::getInstance()->modelAnimate( m_model ); } void GroupWindow::addToGroupClickedEvent() { m_model->addSelectedToGroup( m_groupComboBox->currentIndex() - 1 ); DecalManager::getInstance()->modelAnimate( m_model ); } void GroupWindow::smoothChangedEvent( int val ) { m_model->setGroupSmooth( m_groupComboBox->currentIndex() - 1, val ); QString text = tr( "Smoothness: " ); QString valStr = QString::asprintf( "%03d", (int) ((val / 255.0) * 100.0 ) ); m_smoothLabel->setText( text + valStr ); m_model->calculateNormals(); DecalManager::getInstance()->modelUpdated( m_model ); } void GroupWindow::angleChangedEvent( int val ) { m_model->setGroupAngle( m_groupComboBox->currentIndex() - 1, val ); QString text = tr( "Max Angle: " ); QString valStr = QString::asprintf( "%03d", val ); m_angleLabel->setText( text + valStr ); m_model->calculateNormals(); DecalManager::getInstance()->modelUpdated( m_model ); } void GroupWindow::groupSelectedEvent( int id ) { if ( id > 0 ) { m_smoothSlider->setEnabled( true ); m_smoothSlider->setValue( m_model->getGroupSmooth( id - 1 ) ); m_angleSlider->setEnabled( true ); m_angleSlider->setValue( m_model->getGroupAngle( id - 1 ) ); m_renameButton->setEnabled( true ); m_deleteButton->setEnabled( true ); m_assignAsGroupButton->setEnabled( true ); m_addToGroupButton->setEnabled( true ); m_textureComboBox->setEnabled( true ); int texId = m_model->getGroupTextureId( id - 1 ); m_textureComboBox->setCurrentIndex( texId + 1 ); updateTexture(); } else { m_smoothSlider->setEnabled( false ); m_angleSlider->setEnabled( false ); m_renameButton->setEnabled( false ); m_deleteButton->setEnabled( false ); m_assignAsGroupButton->setEnabled( false ); m_addToGroupButton->setEnabled( false ); m_textureComboBox->setEnabled( false ); } DecalManager::getInstance()->modelAnimate( m_model ); } void GroupWindow::textureSelectedEvent( int id ) { int groupId = m_groupComboBox->currentIndex() - 1; if ( groupId >= 0 ) { m_model->setGroupTextureId( groupId, id - 1 ); } updateTexture(); DecalManager::getInstance()->modelAnimate( m_model ); } void GroupWindow::updateTexture() { m_textureFrame->textureChangedEvent( m_textureComboBox->currentIndex() ); } void GroupWindow::accept() { m_model->operationComplete( tr( "Group changes", "operation complete" ).toUtf8() ); QDialog::accept(); DecalManager::getInstance()->modelUpdated( m_model ); } void GroupWindow::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/groupwin.h000066400000000000000000000033061466047437300165720ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __GROUPWIN_H #define __GROUPWIN_H #include "groupwin.base.h" #include class Model; class GroupWindow : public QDialog, public Ui::GroupWinBase { Q_OBJECT public: GroupWindow( Model * model, QWidget * parent = NULL ); virtual ~GroupWindow(); public slots: void helpNowEvent(); void newClickedEvent(); void renameClickedEvent(); void deleteClickedEvent(); void selectFacesClickedEvent(); void unselectFacesClickedEvent(); void assignAsGroupClickedEvent(); void addToGroupClickedEvent(); void smoothChangedEvent(int); void angleChangedEvent(int); void groupSelectedEvent(int); void textureSelectedEvent(int); void accept(); void reject(); protected: void updateTexture(); Model * m_model; }; #endif // __GROUPWIN_H mm3d-1.3.15/src/implui/helpwin.cc000066400000000000000000000036301466047437300165240ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "helpwin.h" #include "sysconf.h" #include #include HelpWin::HelpWin( const char * document, bool modal, QWidget * parent ) : QDialog( parent ) { setAttribute( Qt::WA_DeleteOnClose ); setModal( modal ); setupUi( this ); #ifdef WIN32 QString source = QString( getDocDirectory().c_str() ) + QString( "\\olh_index.html" ); #else QString source = QString( getDocDirectory().c_str() ) + QString( "/olh_index.html" ); #endif m_text->setSource( QUrl::fromLocalFile( source ) ); if ( document ) { #ifdef WIN32 QString source = QString( getDocDirectory().c_str() ) + QString( "\\" ) + QString( document ); #else QString source = QString( getDocDirectory().c_str() ) + QString( "/" ) + QString( document ); #endif m_text->setSource( QUrl::fromLocalFile( source ) ); } //m_text->home(); m_forwardButton->setEnabled( false ); m_backButton->setEnabled( false ); } HelpWin::~HelpWin() { } mm3d-1.3.15/src/implui/helpwin.h000066400000000000000000000022721466047437300163670ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __HELPWIN_H #define __HELPWIN_H #include "helpwin.base.h" #include class HelpWin : public QDialog, public Ui::HelpWinBase { Q_OBJECT public: HelpWin( const char * document = "olh_index.html", bool modal = false, QWidget * parent = NULL ); virtual ~HelpWin(); protected: }; #endif // __HELPWIN_H mm3d-1.3.15/src/implui/iqeprompt.cc000066400000000000000000000064111466047437300170760ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "iqeprompt.h" #include "iqefilter.h" #include "model.h" #include "helpwin.h" #include #include #include IqePrompt::IqePrompt() : QDialog( NULL ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } IqePrompt::~IqePrompt() { } void IqePrompt::setOptions( IqeFilter::IqeOptions * opts, Model * model ) { m_saveAsPlayer->setEnabled( opts->m_playerSupported ); m_saveAsPlayer->setChecked( opts->m_saveAsPlayer ); m_saveAnimationCfg->setChecked( opts->m_saveAnimationCfg ); m_saveMeshes->setChecked( opts->m_saveMeshes ); m_savePointsJoint->setChecked( opts->m_savePointsJoint ); m_saveSkeleton->setChecked( opts->m_saveSkeleton ); m_saveAnimations->setChecked( opts->m_saveAnimations ); m_animList->clear(); unsigned count = model->getAnimCount( Model::ANIMMODE_SKELETAL ); for ( unsigned t = 0; t < count; t++ ) { m_animList->insertItem( t, QString::fromUtf8( model->getAnimName( Model::ANIMMODE_SKELETAL, t ) ) ); m_animList->item(t)->setSelected( true ); } } void IqePrompt::getOptions( IqeFilter::IqeOptions * opts ) { opts->m_saveAsPlayer = m_saveAsPlayer->isChecked(); opts->m_saveAnimationCfg = m_saveAnimationCfg->isChecked(); opts->m_saveMeshes = m_saveMeshes->isChecked(); opts->m_savePointsJoint= m_savePointsJoint->isChecked(); opts->m_saveSkeleton = m_saveSkeleton->isChecked(); opts->m_saveAnimations = m_saveAnimations->isChecked(); opts->m_animations.clear(); unsigned count = m_animList->count(); for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item( t )->isSelected() ) { opts->m_animations.push_back( t ); } } } void IqePrompt::helpNowEvent() { HelpWin * win = new HelpWin( "olh_iqeprompt.html", true ); win->show(); } bool iqeprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ) { bool rval = false; IqePrompt p; IqeFilter::IqeOptions * opts = dynamic_cast< IqeFilter::IqeOptions * >( o ); if ( opts ) { opts->setOptionsFromModel( model, filename ); p.setOptions( opts, model ); if ( p.exec() ) { rval = true; p.getOptions( opts ); } } else { rval = true; } return rval; } mm3d-1.3.15/src/implui/iqeprompt.h000066400000000000000000000026661466047437300167500ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __IQEPROMPT_H #define __IQEPROMPT_H #include "iqeprompt.base.h" #include "modelfilter.h" #include "iqefilter.h" #include class Model; class IqePrompt : public QDialog, public Ui::IqePromptBase { Q_OBJECT public: IqePrompt(); virtual ~IqePrompt(); void setOptions( IqeFilter::IqeOptions * o, Model * model ); void getOptions( IqeFilter::IqeOptions * o ); public slots: void helpNowEvent(); protected: }; bool iqeprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ); #endif // __IQEPROMPT_H mm3d-1.3.15/src/implui/jointwin.cc000066400000000000000000000153231466047437300167210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "jointwin.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include #include using std::list; #include "model.h" #include "decalmgr.h" #include "log.h" #include "msg.h" #include "modelstatus.h" JointWin::JointWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); for ( int t = 0; t < m_model->getBoneJointCount(); t++ ) { m_jointName->insertItem( 1 + t, QString::fromUtf8( m_model->getBoneJointName(t) ) ); } list joints; m_model->getSelectedBoneJoints( joints ); if ( ! joints.empty() ) { m_jointName->setCurrentIndex( joints.front() ); jointNameSelected( joints.front() ); } else { m_jointName->setCurrentIndex( 0 ); jointNameSelected( 0 ); } } JointWin::~JointWin() { } void JointWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_jointwin.html", true ); win->show(); } void JointWin::jointNameSelected( int index ) { int jointNum = index - 1; m_model->unselectAllBoneJoints(); if ( jointNum >= 0 ) { m_model->selectBoneJoint( jointNum ); m_deleteButton->setEnabled( true ); m_renameButton->setEnabled( true ); m_selectVerticesButton->setEnabled( true ); m_assignVerticesButton->setEnabled( true ); m_addVerticesButton->setEnabled( true ); } else { m_deleteButton->setEnabled( false ); m_renameButton->setEnabled( false ); m_selectVerticesButton->setEnabled( false ); m_assignVerticesButton->setEnabled( false ); m_addVerticesButton->setEnabled( false ); } DecalManager::getInstance()->modelUpdated( m_model ); } void JointWin::deleteClicked() { int jointNum = m_jointName->currentIndex() - 1; if ( jointNum >= 0 && m_model->deleteBoneJoint( jointNum ) ) { m_jointName->removeItem( 1 + jointNum ); m_jointName->setCurrentIndex( 0 ); jointNameSelected( 0 ); } } void JointWin::renameClicked() { int jointNum = m_jointName->currentIndex() - 1; if ( jointNum >= 0 ) { bool ok = false; QString jointName = QInputDialog::getText( this, tr("Rename joint", "window title"), tr("Enter new joint name:"), QLineEdit::Normal, QString::fromUtf8( m_model->getBoneJointName( jointNum ) ), &ok ); if ( ok ) { m_model->setBoneJointName( jointNum, jointName.toUtf8() ); m_jointName->setItemText( 1 + jointNum, jointName ); } } } void JointWin::selectVerticesClicked() { int jointNum = m_jointName->currentIndex() - 1; if ( jointNum >= 0 ) { m_model->unselectAllVertices(); unsigned vcount = m_model->getVertexCount(); for ( unsigned v = 0; v < vcount; v++ ) { Model::InfluenceList l; m_model->getVertexInfluences( v, l ); Model::InfluenceList::iterator it; for ( it = l.begin(); it != l.end(); it++ ) { if ( (*it).m_boneId == jointNum ) { m_model->selectVertex( v ); break; } } } unsigned pcount = m_model->getPointCount(); for ( unsigned p = 0; p < pcount; p++ ) { Model::InfluenceList l; m_model->getPointInfluences( p, l ); Model::InfluenceList::iterator it; for ( it = l.begin(); it != l.end(); it++ ) { if ( (*it).m_boneId == jointNum ) { m_model->selectPoint( p ); break; } } } DecalManager::getInstance()->modelUpdated( m_model ); } } void JointWin::selectUnassignedClicked() { m_model->unselectAllVertices(); unsigned vcount = m_model->getVertexCount(); for ( unsigned v = 0; v < vcount; v++ ) { Model::InfluenceList l; m_model->getVertexInfluences( v, l ); if ( l.empty() ) { m_model->selectVertex( v ); } } unsigned pcount = m_model->getPointCount(); for ( unsigned p = 0; p < pcount; p++ ) { Model::InfluenceList l; m_model->getPointInfluences( p, l ); if ( l.empty() ) { m_model->selectPoint( p ); } } DecalManager::getInstance()->modelUpdated( m_model ); } void JointWin::assignVerticesClicked() { int jointNum = m_jointName->currentIndex() - 1; log_debug( "assignVerticesClicked()\n" ); if ( jointNum >= 0 ) { list posList; m_model->getSelectedPositions( posList ); list::iterator it; log_debug( "assigning %" PORTuSIZE " objects to joint %d\n", posList.size(), jointNum ); for ( it = posList.begin(); it != posList.end(); it++ ) { m_model->setPositionBoneJoint( *it, jointNum ); } } } void JointWin::addVerticesClicked() { int jointNum = m_jointName->currentIndex() - 1; log_debug( "addVerticesClicked()\n" ); if ( jointNum >= 0 ) { list posList; m_model->getSelectedPositions( posList ); list::iterator it; log_debug( "adding %" PORTuSIZE " objects to joint %d\n", posList.size(), jointNum ); for ( it = posList.begin(); it != posList.end(); it++ ) { m_model->addPositionInfluence( *it, jointNum, Model::IT_Custom, 1.0 ); } } } void JointWin::accept() { log_debug( "Joint changes complete\n" ); m_model->operationComplete( tr( "Joint changes", "operation complete" ).toUtf8() ); QDialog::accept(); } void JointWin::reject() { log_debug( "Joint changes canceled\n" ); m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/jointwin.h000066400000000000000000000030311466047437300165540ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __JOINTWIN_H #define __JOINTWIN_H #include "jointwin.base.h" #include class Model; class JointWin : public QDialog, public Ui::JointWinBase { Q_OBJECT public: JointWin( Model * model, QWidget * parent = NULL ); virtual ~JointWin(); public slots: void helpNowEvent(); void deleteClicked(); void renameClicked(); void selectVerticesClicked(); void selectUnassignedClicked(); void assignVerticesClicked(); void addVerticesClicked(); void jointNameSelected( int index ); protected slots: void accept(); void reject(); protected: Model * m_model; }; #endif // __JOINTWIN_H mm3d-1.3.15/src/implui/keycfg.cc000066400000000000000000000502171466047437300163310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include #include "keycfg.h" #include "log.h" #include "qtmain.h" #include "misc.h" #include "mm3dconfig.h" #include "filedatadest.h" #include "filedatasource.h" #include static void _writeDefaultFile( const char * filename ) { FileDataDest dst( filename ); if ( !dst.errorOccurred() ) { const char *msg = "; This file is used to change key bindings for Maverick Model 3D." FILE_NEWLINE ";" FILE_NEWLINE "; To change keyboard shortcuts, add keyboard shortcuts assignments at the bottom " FILE_NEWLINE "; of this file. The format for keyboard shortcuts is:" FILE_NEWLINE ";" FILE_NEWLINE "; mm3d_command_name MODIFIER+Key" FILE_NEWLINE ";" FILE_NEWLINE "; For a full list of MM3D command names, see keycfg.out. The keycfg.out file" FILE_NEWLINE "; is written at the end of every MM3D session and includes all the currently" FILE_NEWLINE "; assigned shortcuts (including default values and user-specified values)." FILE_NEWLINE ";" FILE_NEWLINE "; There can be as many spaces between the command name and the keyboard shortcut" FILE_NEWLINE "; as you want, but there cannot be any spaces in the keyboard shortcut sequence." FILE_NEWLINE ";" FILE_NEWLINE "; Valid modifiers are:" FILE_NEWLINE ";" FILE_NEWLINE "; Ctrl" FILE_NEWLINE "; Alt" FILE_NEWLINE "; Shift" FILE_NEWLINE "; Meta" FILE_NEWLINE ";" FILE_NEWLINE "; Modifiers are not case-sensitive. You can have as many modifiers as you want " FILE_NEWLINE "; (none, all, or any combination)." FILE_NEWLINE ";" FILE_NEWLINE "; Most keys can be entered by typing the actual key (letters, numbers, arithmetic" FILE_NEWLINE "; operators, etc)." FILE_NEWLINE ";" FILE_NEWLINE "; Some special keys:" FILE_NEWLINE ";" FILE_NEWLINE "; Del" FILE_NEWLINE "; PgDown" FILE_NEWLINE "; PgUp" FILE_NEWLINE "; Space" FILE_NEWLINE "; Print (Print Screen key)" FILE_NEWLINE "; Up" FILE_NEWLINE "; Down" FILE_NEWLINE "; Left" FILE_NEWLINE "; Right" FILE_NEWLINE "; Esc" FILE_NEWLINE "; Tab" FILE_NEWLINE "; Enter" FILE_NEWLINE "; Return" FILE_NEWLINE "; Backspace" FILE_NEWLINE "; Insert" FILE_NEWLINE "; Ins" FILE_NEWLINE "; Delete" FILE_NEWLINE "; Del" FILE_NEWLINE "; Home" FILE_NEWLINE "; End" FILE_NEWLINE "; F1 through F12" FILE_NEWLINE ";" FILE_NEWLINE FILE_NEWLINE "; Uncomment the following line to assign Shift+F1 to the Help->Contents menu item" FILE_NEWLINE ";viewwin_help_contents Shift+F1" FILE_NEWLINE FILE_NEWLINE "; Uncomment the following line to remove the keyboard shortcut for File->Save" FILE_NEWLINE ";viewwin_file_save " FILE_NEWLINE; dst.writeString( msg ); } else { log_error( "Couldn't write to key config file: %s\n", filename ); } } KeyConfig g_keyConfig; KeyConfig::KeyConfig() { } KeyConfig::~KeyConfig() { } QKeySequence KeyConfig::getKey( const char * operation ) { KeyDataList::iterator it; for ( it = m_list.begin(); it != m_list.end(); it++ ) { if ( strcasecmp( operation, (*it).operation.c_str() ) == 0 ) { return (*it).key; } } KeyDataT kd; kd.operation = operation; m_list.push_back( kd ); return m_list.back().key; } void KeyConfig::setKey( const char * operation, const QKeySequence & key ) { KeyDataList::iterator it; // Clear any operation key that uses this key (to prevent duplicates) if ( !key.isEmpty() ) { for ( it = m_list.begin(); it != m_list.end(); it++ ) { if ( (*it).key == key ) { (*it).key = 0; } } } for ( it = m_list.begin(); it != m_list.end(); it++ ) { if ( strcasecmp( operation, (*it).operation.c_str() ) == 0 ) { (*it).key = key; return; } } KeyDataT kd; kd.operation = operation; kd.key = key; m_list.push_back( kd ); } void KeyConfig::setDefaultKey( const char * operation, const QKeySequence & key ) { KeyDataList::iterator it; // See if this key is already in use (and ignore this request) for ( it = m_list.begin(); it != m_list.end(); it++ ) { if ( (*it).key == key ) { return; } } // See if this action is already defined (and ignore this request) for ( it = m_list.begin(); it != m_list.end(); it++ ) { if ( strcasecmp( operation, (*it).operation.c_str() ) == 0 ) { return; } } // Key combo not in use, action not already defined, save default KeyDataT kd; kd.operation = operation; kd.key = key; m_list.push_back( kd ); } bool KeyConfig::saveFile( const char * filename ) { FileDataDest dst( filename ); if ( !dst.errorOccurred() ) { KeyDataList::iterator it; for ( it = m_list.begin(); it != m_list.end(); it++ ) { QString keyStr = (*it).key.toString(); if ( keyStr.isNull() ) { dst.writePrintf( "%s " FILE_NEWLINE, (*it).operation.c_str() ); } else { dst.writePrintf( "%s %s" FILE_NEWLINE, (*it).operation.c_str(), (const char *) keyStr.toUtf8() ); } } return true; } else { log_error( "Couldn't write to key config file: %s\n", filename ); } return false; } bool KeyConfig::loadFile( const char * filename ) { log_debug( "loading keyboard shortcut config file:\n" ); log_debug( " %s\n", filename ); FileDataSource src( filename ); if ( !src.errorOccurred() ) { log_debug( "reading file...\n" ); m_list.clear(); char line[1024]; while ( src.readLine( line, sizeof(line) ) ) { chomp( line ); if ( isspace(line[0]) || line[0] == ';' || line[0] == '#' || strncmp(line, "//", 2 ) == 0 ) { // comment, ignore it } else if ( line[0] ) { // not blank or comment line, parse it char * space = strchr( line, ' ' ); if ( space ) { std::string op; op.assign( line, space - line ); char * str = space; QKeySequence key; if ( op.size() > 0 ) { while ( isspace( str[0] ) ) { str++; } // empty definitions are valid QString s( str ); QKeySequence k( s ); setKey( op.c_str(), k ); } } else { std::string op = line; if ( op.size() > 0 ) { QKeySequence k; setKey( op.c_str(), k ); } } } } return true; } else { log_debug( "keyboard config file does not exist, creating...\n" ); _writeDefaultFile( filename ); } return false; } /* int KeyConfig::getSpecialKey( const char * keyName ) { for ( unsigned int k = 0; k < KEY_NAMES; k++ ) { if ( strcasecmp( keyName, _special[k].operation.c_str() ) == 0 ) { return _special[k].key; } } return 0; } */ /* std::string KeyConfig::getSpecialKeyName( int key ) { for ( unsigned int k = 0; k < KEY_NAMES; k++ ) { if ( key == _special[k].key ) { return _special[k].operation; } } return ""; } */ bool keycfg_load_file( const char * filename ) { return g_keyConfig.loadFile( filename ); } bool keycfg_save_file( const char * filename ) { return g_keyConfig.saveFile( filename ); } void keycfg_set_defaults() { QApplication * a = ui_getapp(); // Tools g_keyConfig.setDefaultKey( "tool_snap_to_grid", 0 ); g_keyConfig.setDefaultKey( "tool_snap_to_vertex", 0 ); g_keyConfig.setDefaultKey( "tool_select_vertices", QKeySequence( a->translate( "KeyConfig", "V", "Select Vertices Tool Shortcut" )) ); g_keyConfig.setDefaultKey( "tool_select_faces", QKeySequence( a->translate( "KeyConfig", "F", "Select Faces Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_select_connected_mesh", QKeySequence( a->translate( "KeyConfig", "C", "Select Connected Mesh Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_select_groups", QKeySequence( a->translate( "KeyConfig", "G", "Select Groups Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_select_bone_joints", QKeySequence( a->translate( "KeyConfig", "B", "Select Bone Joints Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_select_points", QKeySequence( a->translate( "KeyConfig", "T", "Select Points Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_select_projections", 0 ); g_keyConfig.setDefaultKey( "tool_move", QKeySequence( a->translate( "KeyConfig", "M", "Move Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_rotate", QKeySequence( a->translate( "KeyConfig", "R", "Rotate Tool Shortcut")) ); g_keyConfig.setDefaultKey( "tool_scale", 0 ); g_keyConfig.setDefaultKey( "tool_shear", 0 ); g_keyConfig.setDefaultKey( "tool_extrude", 0 ); g_keyConfig.setDefaultKey( "tool_drag_vertex_on_edge", 0 ); g_keyConfig.setDefaultKey( "tool_attract_near", 0 ); g_keyConfig.setDefaultKey( "tool_attract_far", 0 ); g_keyConfig.setDefaultKey( "tool_move_background_image", 0 ); g_keyConfig.setDefaultKey( "tool_scale_background_image", 0 ); g_keyConfig.setDefaultKey( "tool_create_vertex", 0 ); g_keyConfig.setDefaultKey( "tool_create_rectangle", 0 ); g_keyConfig.setDefaultKey( "tool_create_cube", 0 ); g_keyConfig.setDefaultKey( "tool_create_ellipsoid", 0 ); g_keyConfig.setDefaultKey( "tool_create_cylinder", 0 ); g_keyConfig.setDefaultKey( "tool_create_torus", 0 ); g_keyConfig.setDefaultKey( "tool_create_polygon", 0 ); g_keyConfig.setDefaultKey( "tool_create_bone_joint", 0 ); g_keyConfig.setDefaultKey( "tool_create_point", 0 ); g_keyConfig.setDefaultKey( "tool_create_projection", 0 ); // Commands g_keyConfig.setDefaultKey( "cmd_invert_selection", 0 ); g_keyConfig.setDefaultKey( "cmd_select_free_vertices", 0 ); g_keyConfig.setDefaultKey( "cmd_hide_hide_unselected", QKeySequence( a->translate( "KeyConfig", "H", "Hide Unselected Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_hide_hide_selected", QKeySequence( a->translate( "KeyConfig", "Shift+H", "Hide Selected Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_hide_unhide_all", QKeySequence( a->translate( "KeyConfig", "Shift+U", "Unhide All Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_hide", 0 ); g_keyConfig.setDefaultKey( "cmd_delete", QKeySequence( a->translate( "KeyConfig", "Delete", "Delete Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_duplicate", QKeySequence( a->translate( "KeyConfig", "Ctrl+D", "Duplicate Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_copy_selected_to_clipboard", QKeySequence( a->translate( "KeyConfig", "Ctrl+C", "Copy to Clipboard Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_paste_from_clipboard", QKeySequence( a->translate( "KeyConfig", "Ctrl+V", "Paste from Clipboard Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_flip_flip_x", 0 ); g_keyConfig.setDefaultKey( "cmd_flip_flip_y", 0 ); g_keyConfig.setDefaultKey( "cmd_flip_flip_z", 0 ); g_keyConfig.setDefaultKey( "cmd_flip", 0 ); g_keyConfig.setDefaultKey( "cmd_flatten_flatten_x", 0 ); g_keyConfig.setDefaultKey( "cmd_flatten_flatten_y", 0 ); g_keyConfig.setDefaultKey( "cmd_flatten_flatten_z", 0 ); g_keyConfig.setDefaultKey( "cmd_flatten", 0 ); g_keyConfig.setDefaultKey( "cmd_extrude", QKeySequence( a->translate( "KeyConfig", "Insert", "Extrude Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_invert_normals", 0 ); g_keyConfig.setDefaultKey( "cmd_weld_vertices", QKeySequence( a->translate( "KeyConfig", "Ctrl+W", "Weld Command Shortcut")) ); g_keyConfig.setDefaultKey( "cmd_unweld_vertices", 0 ); g_keyConfig.setDefaultKey( "cmd_snap_vertices_together_snap_all_selected", 0 ); g_keyConfig.setDefaultKey( "cmd_snap_vertices_together_snap_nearest_selected", 0 ); g_keyConfig.setDefaultKey( "cmd_snap_vertices_together_snap_all_and_weld", 0 ); g_keyConfig.setDefaultKey( "cmd_snap_vertices_together_snap_nearest_and_weld", 0 ); g_keyConfig.setDefaultKey( "cmd_snap_vertices_together", 0 ); g_keyConfig.setDefaultKey( "cmd_edge_turn", 0 ); g_keyConfig.setDefaultKey( "cmd_edge_divide", 0 ); g_keyConfig.setDefaultKey( "cmd_subdivide_faces", 0 ); g_keyConfig.setDefaultKey( "cmd_make_face_from_vertices", 0 ); g_keyConfig.setDefaultKey( "cmd_cap_holes", 0 ); g_keyConfig.setDefaultKey( "cmd_rotate_texture_coordinates_face", 0 ); g_keyConfig.setDefaultKey( "cmd_rotate_texture_coordinates_group", 0 ); g_keyConfig.setDefaultKey( "cmd_rotate_texture_coordinates", 0 ); g_keyConfig.setDefaultKey( "cmd_align_selected", 0 ); g_keyConfig.setDefaultKey( "cmd_spherify", 0 ); g_keyConfig.setDefaultKey( "cmd_simplify_mesh", 0 ); // View Window g_keyConfig.setDefaultKey( "viewwin_file_new", QKeySequence( a->translate( "KeyConfig", "Ctrl+N", "File | New Window Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_file_open", QKeySequence( a->translate( "KeyConfig", "Ctrl+O", "File | Open Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_file_save", QKeySequence( a->translate( "KeyConfig", "Ctrl+S", "File | Save Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_file_save_as", 0 ); g_keyConfig.setDefaultKey( "viewwin_file_export", 0 ); g_keyConfig.setDefaultKey( "viewwin_file_export_selected", 0 ); g_keyConfig.setDefaultKey( "viewwin_file_run_script", 0 ); g_keyConfig.setDefaultKey( "viewwin_file_plugins", 0 ); g_keyConfig.setDefaultKey( "viewwin_file_close", 0 ); g_keyConfig.setDefaultKey( "viewwin_file_quit", QKeySequence( a->translate( "KeyConfig", "Ctrl+Q", "File | Quit Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_view_render_joints_hide", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_joints_lines", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_joints_bones", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_projections_show", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_projections_hide", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_badtex_red", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_badtex_blank", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_3d_lines_show", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_3d_lines_hide", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_backface_show", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_render_backface_hide", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_frame_all", QKeySequence( a->translate( "KeyConfig", "Home", "View | Frame All Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_view_frame_selected", QKeySequence( a->translate( "KeyConfig", "Shift+Home", "View | Frame Selected Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_view_show_properties", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3d_wireframe", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3d_flat", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3d_smooth", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3d_textured", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3d_alpha", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_ortho_wireframe", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_ortho_flat", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_ortho_smooth", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_ortho_textured", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_ortho_alpha", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_1", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_1x2", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_2x1", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_2x2", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_2x3", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3x2", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_3x3", 0 ); g_keyConfig.setDefaultKey( "viewwin_view_viewport_settings", 0 ); g_keyConfig.setDefaultKey( "viewwin_model_edit_meta_data", 0 ); g_keyConfig.setDefaultKey( "viewwin_model_transform", 0 ); g_keyConfig.setDefaultKey( "viewwin_model_boolean_operation", 0 ); g_keyConfig.setDefaultKey( "viewwin_model_set_background_image", 0 ); g_keyConfig.setDefaultKey( "viewwin_model_merge", 0 ); g_keyConfig.setDefaultKey( "viewwin_model_import_animations", 0 ); g_keyConfig.setDefaultKey( "viewwin_groups_edit_groups", QKeySequence( a->translate( "KeyConfig", "Ctrl+G", "Groups | Edit Groups Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_groups_edit_materials", QKeySequence( a->translate( "KeyConfig", "Ctrl+M", "Groups | Edit Materials Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_groups_cleanup", 0 ); g_keyConfig.setDefaultKey( "viewwin_groups_reload_textures", 0 ); g_keyConfig.setDefaultKey( "viewwin_groups_edit_projection", 0 ); g_keyConfig.setDefaultKey( "viewwin_groups_edit_texture_coordinates", QKeySequence( a->translate( "KeyConfig", "Ctrl+E", "Groups | Edit Texture Coordinates Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_groups_paint_texture", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_edit_joints", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_assign_selected", QKeySequence( a->translate( "KeyConfig", "Ctrl+B", "Joints | Assign Selected Shortcut")) ); g_keyConfig.setDefaultKey( "viewwin_joints_auto_assign_selected", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_remove_influences", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_remove_joint", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_make_single_influence", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_select_joint_influences", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_select_influenced_vertices", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_select_influenced_points", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_select_unassigned_vertices", 0 ); g_keyConfig.setDefaultKey( "viewwin_joints_select_unassigned_points", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_start_mode", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_stop_mode", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_animation_sets", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_save_images", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_frame_copy", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_frame_paste", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_frame_clear", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_selected_copy", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_selected_paste", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_set_rotation", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_set_translation", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_clear_rotation", 0 ); g_keyConfig.setDefaultKey( "viewwin_anim_clear_translation", 0 ); g_keyConfig.setDefaultKey( "viewwin_help_contents", 0 ); g_keyConfig.setDefaultKey( "viewwin_help_license", 0 ); g_keyConfig.setDefaultKey( "viewwin_help_about", 0 ); } mm3d-1.3.15/src/implui/keycfg.h000066400000000000000000000035141466047437300161710ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __KEYCFG_H #define __KEYCFG_H #include #include #include class KeyConfig { public: KeyConfig(); virtual ~KeyConfig(); QKeySequence getKey( const char * operation ); void setKey( const char * operation, const QKeySequence & key ); void setDefaultKey( const char * operation, const QKeySequence & key ); bool saveFile( const char * filename ); bool loadFile( const char * filename ); //static int getSpecialKey( const char * keyName ); //static std::string getSpecialKeyName( int key ); struct _KeyData_t { std::string operation; QKeySequence key; }; typedef struct _KeyData_t KeyDataT; typedef std::list< KeyDataT > KeyDataList; protected: KeyDataList m_list; }; extern KeyConfig g_keyConfig; bool keycfg_load_file( const char * filename ); bool keycfg_save_file( const char * filename ); void keycfg_set_defaults(); #endif // __KEYCFG_H mm3d-1.3.15/src/implui/keyvaluewin.cc000066400000000000000000000031341466047437300174200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "keyvaluewin.h" #include "model.h" #include "glmath.h" #include #include #include #include #include #include #include KeyValueWindow::KeyValueWindow( QTreeWidgetItem * item, QWidget * parent ) : QDialog( parent ), m_item( item ) { setupUi( this ); setModal( true ); m_nameEdit->setText( item->text(0) ); m_valueEdit->setText( item->text(1) ); } KeyValueWindow::~KeyValueWindow() { } void KeyValueWindow::accept() { m_item->setText( 0, m_nameEdit->text() ); m_item->setText( 1, m_valueEdit->text() ); QDialog::accept(); } void KeyValueWindow::reject() { QDialog::reject(); } mm3d-1.3.15/src/implui/keyvaluewin.h000066400000000000000000000024701466047437300172640ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __KEYVALUEWIN_H #define __KEYVALUEWIN_H #include "keyvaluewin.base.h" #include class QTreeWidgetItem; class KeyValueWindow : public QDialog, public Ui::KeyValueWindowBase { Q_OBJECT public: KeyValueWindow( QTreeWidgetItem * item, QWidget * parent = NULL ); virtual ~KeyValueWindow(); public slots: void accept(); void reject(); protected: QTreeWidgetItem * m_item; }; #endif // __KEYVALUEWIN_H mm3d-1.3.15/src/implui/licensewin.cc000066400000000000000000000027331466047437300172210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "licensewin.h" #include "sysconf.h" #include LicenseWin::LicenseWin( QWidget * parent ) : QDialog( parent ) { setAttribute( Qt::WA_DeleteOnClose ); setModal( false ); setupUi( this ); resize( 600, 400 ); setWindowTitle( tr("GNU General Public License") ); #ifdef WIN32 QString source = QString( getDocDirectory().c_str() ) + QString( "/olh_license.html" ); #else QString source = QString( getDocDirectory().c_str() ) + QString( "/olh_license.html" ); #endif m_text->setSource( QUrl::fromLocalFile( source ) ); } LicenseWin::~LicenseWin() { } mm3d-1.3.15/src/implui/licensewin.h000066400000000000000000000022171466047437300170600ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __LICENSEWIN_H #define __LICENSEWIN_H #include "textwin.base.h" #include class LicenseWin : public QDialog, public Ui::TextWinBase { Q_OBJECT public: LicenseWin( QWidget * parent = NULL ); virtual ~LicenseWin(); protected: }; #endif // __LICENSEWIN_H mm3d-1.3.15/src/implui/mapdirection.cc000066400000000000000000000024331466047437300175340ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "mapdirection.h" #include MapDirection::MapDirection( QWidget * parent ) : QDialog( parent ) { setupUi( this ); setModal( true ); m_directionComboBox->setCurrentIndex( 0 ); } MapDirection::~MapDirection() { } int MapDirection::getMapDirection() { return m_directionComboBox->currentIndex(); } void MapDirection::setMapDirection( int index ) { m_directionComboBox->setCurrentIndex( index ); } mm3d-1.3.15/src/implui/mapdirection.h000066400000000000000000000023301466047437300173720ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MAPDIRECTION_H #define __MAPDIRECTION_H #include "mapdirection.base.h" #include class MapDirection : public QDialog, public Ui::MapDirectionBase { public: MapDirection( QWidget * parent = NULL ); virtual ~MapDirection(); void setMapDirection( int ); int getMapDirection(); protected: }; #endif // __MAPDIRECTION_H mm3d-1.3.15/src/implui/md3prompt.cc000066400000000000000000000044561466047437300170120ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "md3prompt.h" #include "md3filter.h" #include "model.h" #include "helpwin.h" #include #include #include Md3Prompt::Md3Prompt() : QDialog( NULL ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } Md3Prompt::~Md3Prompt() { } void Md3Prompt::setOptions( Md3Filter::Md3Options * opts, Model * model ) { m_saveAsPlayer->setEnabled( opts->m_playerSupported ); m_saveAsPlayer->setChecked( opts->m_saveAsPlayer ); m_saveAnimationCfg->setChecked( opts->m_saveAnimationCfg ); } void Md3Prompt::getOptions( Md3Filter::Md3Options * opts ) { opts->m_saveAsPlayer = m_saveAsPlayer->isChecked(); opts->m_saveAnimationCfg = m_saveAnimationCfg->isChecked(); } void Md3Prompt::helpNowEvent() { HelpWin * win = new HelpWin( "olh_quakemd3prompt.html", true ); win->show(); } bool md3prompt_show( Model * model, const char * const filename, ModelFilter::Options * o ) { bool rval = false; Md3Prompt p; Md3Filter::Md3Options * opts = dynamic_cast< Md3Filter::Md3Options * >( o ); if ( opts ) { opts->setOptionsFromModel( model, filename ); p.setOptions( opts, model ); if ( p.exec() ) { rval = true; p.getOptions( opts ); } } else { rval = true; } return rval; } mm3d-1.3.15/src/implui/md3prompt.h000066400000000000000000000026661466047437300166550ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MD3PROMPT_H #define __MD3PROMPT_H #include "md3prompt.base.h" #include "modelfilter.h" #include "md3filter.h" #include class Model; class Md3Prompt : public QDialog, public Ui::Md3PromptBase { Q_OBJECT public: Md3Prompt(); virtual ~Md3Prompt(); void setOptions( Md3Filter::Md3Options * o, Model * model ); void getOptions( Md3Filter::Md3Options * o ); public slots: void helpNowEvent(); protected: }; bool md3prompt_show( Model * model, const char * const filename, ModelFilter::Options * o ); #endif // __MD3PROMPT_H mm3d-1.3.15/src/implui/mergewin.cc000066400000000000000000000057311466047437300166770ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "mergewin.h" #include "model.h" #include "glmath.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include #include #include #include MergeWindow::MergeWindow( Model * model, Model * existingModel, QWidget * parent ) : QDialog( parent ), m_model( model ), m_existingModel( existingModel ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); int t; // index 0 is for ( t = 0; t < m_existingModel->getPointCount(); t++ ) { m_pointName->insertItem( t + 1, QString::fromUtf8( m_existingModel->getPointName(t) ) ); } list points; m_existingModel->getSelectedPoints( points ); if ( ! points.empty() ) { m_pointName->setCurrentIndex( points.front() + 1 ); } else { m_pointName->setCurrentIndex( 0 ); } } MergeWindow::~MergeWindow() { } void MergeWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_mergewin.html", true ); win->show(); } int MergeWindow::getPoint() { return m_pointName->currentIndex() - 1; } void MergeWindow::getRotation( double * vec ) { if ( vec ) { vec[0] = m_rotX->text().toDouble() * PIOVER180; vec[1] = m_rotY->text().toDouble() * PIOVER180; vec[2] = m_rotZ->text().toDouble() * PIOVER180; } } void MergeWindow::getTranslation( double * vec ) { if ( vec ) { vec[0] = m_transX->text().toDouble(); vec[1] = m_transY->text().toDouble(); vec[2] = m_transZ->text().toDouble(); } } void MergeWindow::includeAnimEvent( bool o ) { m_animMerge->setEnabled( o ); m_animAppend->setEnabled( o ); } void MergeWindow::accept() { m_model->operationComplete( tr( "Merge models", "operation complete" ).toUtf8() ); QDialog::accept(); } void MergeWindow::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/mergewin.h000066400000000000000000000033331466047437300165350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MERGEWIN_H #define __MERGEWIN_H #include "mergewin.base.h" #include class Model; class MergeWindow : public QDialog, public Ui::MergeWinBase { Q_OBJECT public: MergeWindow( Model * model, Model * existingModel, QWidget * parent = NULL ); virtual ~MergeWindow(); int getPoint(); void getRotation( double * vec ); void getTranslation( double * vec ); bool getIncludeTexture() { return m_textureInclude->isChecked(); }; bool getIncludeAnimation() { return m_animInclude->isChecked(); }; bool getAnimationMerge() { return m_animInclude->isChecked() && m_animMerge->isChecked(); }; public slots: void helpNowEvent(); void includeAnimEvent( bool o ); void accept(); void reject(); protected: Model * m_model; Model * m_existingModel; }; #endif // __MERGEWIN_H mm3d-1.3.15/src/implui/metawin.cc000066400000000000000000000064721466047437300165310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "metawin.h" #include "model.h" #include "keyvaluewin.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include #include #include #include #include MetaWindow::MetaWindow( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); m_list->header()->setSectionsClickable( false ); m_list->header()->setSectionsMovable( false ); unsigned count = m_model->getMetaDataCount(); for ( unsigned int m = 0; m < count; m++ ) { char key[1024]; char value[1024]; m_model->getMetaData( m, key, sizeof(key), value, sizeof(value) ); QTreeWidgetItem * item = new QTreeWidgetItem( m_list ); item->setText( 0, QString::fromUtf8( key ) ); item->setText( 1, QString::fromUtf8( value ) ); } QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } MetaWindow::~MetaWindow() { } void MetaWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_metawin.html", true ); win->show(); } void MetaWindow::newClicked() { QTreeWidgetItem * item = new QTreeWidgetItem( m_list ); item->setText( 0, tr("Name", "meta value key name") ); item->setText( 1, tr("Value", "meta value 'value'") ); int count = m_list->topLevelItemCount(); for ( int i = 0; i < count; ++i ) { m_list->topLevelItem( i )->setSelected( false ); } item->setSelected( true ); m_list->setCurrentItem( item ); } void MetaWindow::deleteClicked() { delete m_list->currentItem(); } void MetaWindow::editItemEvent( QTreeWidgetItem * item, int col ) { KeyValueWindow w( item ); w.exec(); } void MetaWindow::accept() { m_model->clearMetaData(); int count = m_list->topLevelItemCount(); for ( int i = 0; i < count; ++i ) { QTreeWidgetItem * item = m_list->topLevelItem(i); m_model->addMetaData( (const char *) item->text(0).toUtf8(), (const char *) item->text(1).toUtf8() ); } m_model->operationComplete( tr( "Change meta data", "operation complete" ).toUtf8() ); QDialog::accept(); } void MetaWindow::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/metawin.h000066400000000000000000000026331466047437300163660ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __METAWIN_H #define __METAWIN_H #include "metawin.base.h" #include class Model; class QTreeWidgetItem; class MetaWindow : public QDialog, public Ui::MetaWindowBase { Q_OBJECT public: MetaWindow( Model *, QWidget * parent = NULL ); virtual ~MetaWindow(); public slots: void helpNowEvent(); void newClicked(); void deleteClicked(); void editItemEvent( QTreeWidgetItem * item, int col ); void accept(); void reject(); protected: Model * m_model; }; #endif // __METAWIN_H mm3d-1.3.15/src/implui/ms3dprompt.cc000066400000000000000000000101121466047437300171570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "ms3dprompt.h" #include "ms3dfilter.h" #include "model.h" #include "mm3dport.h" #include "helpwin.h" #include #include #include Ms3dPrompt::Ms3dPrompt() : QDialog( NULL ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } Ms3dPrompt::~Ms3dPrompt() { } void Ms3dPrompt::setOptions( Ms3dFilter::Ms3dOptions * opts ) { switch ( opts->m_subVersion ) { case 0: default: m_subVersion0->setChecked( true ); break; case 1: m_subVersion1->setChecked( true ); break; case 2: m_subVersion2->setChecked( true ); break; case 3: m_subVersion3->setChecked( true ); break; } char str[20]; PORT_snprintf( str, sizeof(str), "%X", opts->m_vertexExtra ); m_vertexExtra->setText( str ); PORT_snprintf( str, sizeof(str), "%X", opts->m_vertexExtra2 ); m_vertexExtra2->setText( str ); // TODO joint color //PORT_snprintf( str, sizeof(str), "%X", opts->m_jointColor ); //m_jointColor->setText( str ); updateExtraEnabled(); } void Ms3dPrompt::getOptions( Ms3dFilter::Ms3dOptions * opts ) { opts->m_subVersion = 0; if ( m_subVersion1->isChecked() ) opts->m_subVersion = 1; if ( m_subVersion2->isChecked() ) opts->m_subVersion = 2; if ( m_subVersion3->isChecked() ) opts->m_subVersion = 3; uint32_t val = 0xffffffff; sscanf( m_vertexExtra->text().toUtf8(), "%X", &val); opts->m_vertexExtra = val; val = 0xffffffff; sscanf( m_vertexExtra2->text().toUtf8(), "%X", &val); opts->m_vertexExtra2 = val; // TODO joint color //val = 0xffffffff; //sscanf( m_jointColor->text().toUtf8(), "%X", &val); //opts->m_vertexExtra = val; } void Ms3dPrompt::helpNowEvent() { HelpWin * win = new HelpWin( "olh_ms3dprompt.html", true ); win->show(); } void Ms3dPrompt::subVersionChangedEvent() { updateExtraEnabled(); } void Ms3dPrompt::updateExtraEnabled() { int subVersion = 0; if ( m_subVersion1->isChecked() ) subVersion = 1; if ( m_subVersion2->isChecked() ) subVersion = 2; if ( m_subVersion3->isChecked() ) subVersion = 3; m_vertexExtra->setEnabled( subVersion >= 2 ); m_vertexExtra2->setEnabled( subVersion >= 3 ); // TODO joint color //m_jointColor->setEnabled( !m_jointSubVersion0->isChecked() ); } // This function takes a ModelFilter::Options argument, downcasts it // to an Ms3dOptions object, and uses it to prompt the user for // options for the MS3D file filter. // // This function is registered with the Ms3dFilter class when the // filter is created in stdfilters.cc. bool ms3dprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ) { bool rval = false; Ms3dPrompt p; Ms3dFilter::Ms3dOptions * opts = dynamic_cast< Ms3dFilter::Ms3dOptions * >( o ); if ( opts ) { opts->setOptionsFromModel( model ); p.setOptions( opts ); if ( p.exec() ) { rval = true; p.getOptions( opts ); } } else { rval = true; } return rval; } mm3d-1.3.15/src/implui/ms3dprompt.h000066400000000000000000000027741466047437300170400ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MS3DPROMPT_H #define __MS3DPROMPT_H #include "ms3dprompt.base.h" #include "modelfilter.h" #include "ms3dfilter.h" #include class Model; class Ms3dPrompt : public QDialog, public Ui::Ms3dPromptBase { Q_OBJECT public: Ms3dPrompt(); virtual ~Ms3dPrompt(); void setOptions( Ms3dFilter::Ms3dOptions * o ); void getOptions( Ms3dFilter::Ms3dOptions * o ); public slots: void helpNowEvent(); void subVersionChangedEvent(); protected: void updateExtraEnabled(); }; bool ms3dprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ); #endif // __MS3DPROMPT_H mm3d-1.3.15/src/implui/msgqt.cc000066400000000000000000000106541466047437300162150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "msg.h" #include #include extern "C" void msgqt_info( const char * str ) { QMessageBox::information( NULL, QString("Maverick Model 3D"), QString::fromUtf8(str), QMessageBox::Ok, 0 ); } extern "C" void msgqt_warning( const char * str ) { QMessageBox::warning( NULL, QString("Maverick Model 3D"), QString::fromUtf8(str), QMessageBox::Ok, 0 ); } extern "C" void msgqt_error( const char * str ) { QMessageBox::critical( NULL, QString("Maverick Model 3D"), QString::fromUtf8(str), QMessageBox::Ok, 0 ); } typedef enum { MsgInfo, MsgWarning, MsgError } MessageType; static char _msgqt_info_common( MessageType type, const QString & str, const char * opts ) { int button[3]; bool done = false; for ( int t = 0; t < 3; t++ ) { if( ! done && opts[t] ) { switch ( toupper( opts[t] ) ) { case 'Y': button[t] = QMessageBox::Yes; break; case 'N': button[t] = QMessageBox::No; break; case 'C': button[t] = QMessageBox::Cancel | QMessageBox::Escape; break; case 'O': button[t] = QMessageBox::Ok; break; case 'A': button[t] = QMessageBox::Abort; break; case 'R': button[t] = QMessageBox::Retry; break; case 'I': button[t] = QMessageBox::Ignore; break; default: button[t] = QMessageBox::NoButton; break; } if ( isupper( opts[t] ) ) { button[t] |= QMessageBox::Default; } } else { done = true; button[t] = 0; } } int result = 0; switch ( type ) { case MsgError: result = QMessageBox::critical( NULL, QString("Maverick Model 3D"), str, button[0], button[1], button[2] ); break; case MsgWarning: result = QMessageBox::warning( NULL, QString("Maverick Model 3D"), str, button[0], button[1], button[2] ); break; case MsgInfo: default: result = QMessageBox::information( NULL, QString("Maverick Model 3D"), str, button[0], button[1], button[2] ); break; } char rval = '!'; switch ( result ) { case QMessageBox::Ok: rval = 'O'; break; case QMessageBox::Yes: rval = 'Y'; break; case QMessageBox::No: rval = 'N'; break; case QMessageBox::Abort: rval = 'A'; break; case QMessageBox::Retry: rval = 'R'; break; case QMessageBox::Ignore: rval = 'I'; break; case QMessageBox::Cancel: default: rval = 'C'; break; } return rval; } // Do you want to save first (yes, no, cancel) [Y/n/c]? // Do you want to save first (abort, retry, ignore) [A/r/i]? extern "C" char msgqt_info_prompt( const char * str, const char * opts ) { return _msgqt_info_common( MsgInfo, QString::fromUtf8(str), opts ); } extern "C" char msgqt_warning_prompt( const char * str, const char * opts ) { return _msgqt_info_common( MsgWarning, QString::fromUtf8(str), opts ); } extern "C" char msgqt_error_prompt( const char * str, const char * opts ) { return _msgqt_info_common( MsgError, QString::fromUtf8(str), opts ); } void init_msgqt() { msg_register( msgqt_info, msgqt_warning, msgqt_error ); msg_register_prompt( msgqt_info_prompt, msgqt_warning_prompt, msgqt_error_prompt ); } mm3d-1.3.15/src/implui/msgqt.h000066400000000000000000000016611466047437300160550ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MSGQT_H #define __MSGQT_H extern void init_msgqt(); #endif // __MSGQT_H mm3d-1.3.15/src/implui/mview.cc000066400000000000000000000057171466047437300162150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "mview.h" #include "modelviewport.h" #include "viewpanel.h" #include "log.h" #include "decalmgr.h" #include #include #include #include ModelView::ModelView( Toolbox * toolbox, QWidget * parent ) : QWidget( parent ), m_toolbox( toolbox ) { setupUi( this ); QString zoomStr = QString::asprintf( "%f", m_modelView->getZoomLevel() ); m_zoomInput->setText( zoomStr ); connect( m_modelView, SIGNAL(viewDirectionChanged(int)), this, SLOT(setViewDirection(int))); ViewPanel * panel = dynamic_cast(parent); if ( panel ) { connect( m_modelView, SIGNAL(modelUpdated()), panel, SLOT(modelUpdatedEvent())); } else { log_error( "cast failed, not connecting\n" ); } } ModelView::~ModelView() { DecalManager::getInstance()->unregisterToolParent( m_modelView ); } void ModelView::freeTextures() { m_modelView->freeTextures(); } void ModelView::setViewDirection( int dir ) { m_viewInput->setCurrentIndex( dir ); m_modelView->viewChangeEvent( dir ); } void ModelView::zoomLevelEnterEvent() { m_modelView->setZoomLevel( m_zoomInput->text().toDouble() ); } void ModelView::zoomInEvent() { m_modelView->zoomIn(); } void ModelView::zoomOutEvent() { m_modelView->zoomOut(); } void ModelView::setModel( Model * model ) { if ( model == NULL ) { DecalManager::getInstance()->unregisterToolParent( m_modelView ); } m_modelView->setModel( model ); m_modelView->setToolbox( m_toolbox ); if ( model ) { DecalManager::getInstance()->registerToolParent( m_modelView ); } } void ModelView::updateView() { m_modelView->updateView(); } unsigned ModelView::getViewDirection() { return m_viewInput->currentIndex(); } QString ModelView::getViewDirectionLabel() { return m_viewInput->currentText(); } void ModelView::copyContentsToTexture( Texture * tex ) { m_modelView->copyContentsToTexture( tex ); } void ModelView::updateCaptureGL() { m_modelView->updateCaptureGL(); } QImage ModelView::grabFramebuffer() { return m_modelView->grabFramebuffer(); } mm3d-1.3.15/src/implui/mview.h000066400000000000000000000035711466047437300160530ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MVIEW_H #define __MVIEW_H #include "modelview.base.h" #include "modelviewport.h" #include class Model; class Toolbox; class Texture; class ModelView : public QWidget, public Ui::ModelViewBase { Q_OBJECT public: ModelView( Toolbox * toolbox, QWidget * parent = NULL ); virtual ~ModelView(); void freeTextures(); void setModel( Model * model ); void frameArea( double x1, double y1, double z1, double x2, double y2, double z2 ) { m_modelView->frameArea( x1, y1, z1, x2, y2, z2 ); }; void updateView(); unsigned getViewDirection(); QString getViewDirectionLabel(); void copyContentsToTexture( Texture * tex ); void updateCaptureGL(); QImage grabFramebuffer(); ModelViewport * getModelViewport() { return m_modelView; }; public slots: void setViewDirection( int dir ); void zoomLevelEnterEvent(); void zoomInEvent(); void zoomOutEvent(); protected: Toolbox * m_toolbox; }; #endif // __MVIEW_H mm3d-1.3.15/src/implui/newanim.cc000066400000000000000000000044151466047437300165160ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "newanim.h" #include #include #include NewAnim::NewAnim( QWidget * parent ) : QDialog( parent ) { setModal( true ); setupUi( this ); m_name->setText( QString("") ); m_okButton->setEnabled( false ); } NewAnim::~NewAnim() { } void NewAnim::editMode() { setWindowTitle( tr( "Edit Animation" ) ); m_skeletal->setEnabled( false ); m_frame->setEnabled( false ); } QString NewAnim::getAnimName() { return m_name->text(); } void NewAnim::setAnimName( const QString &name ) { m_name->setText( name ); } bool NewAnim::isSkeletal() { return m_skeletal->isChecked(); } void NewAnim::setSkeletal( bool o ) { if ( o ) m_skeletal->setChecked( true ); else m_frame->setChecked( true ); } void NewAnim::nameChangedEvent() { if ( m_name->text().length() == 0 ) { m_okButton->setEnabled( false ); } else { m_okButton->setEnabled( true ); } } unsigned NewAnim::getAnimFrameCount() { return m_frames->value(); } void NewAnim::setAnimFrameCount( unsigned value ) { m_frames->setValue( value ); } double NewAnim::getAnimFPS() { return m_fps->text().toDouble(); } void NewAnim::setAnimFPS( double fps ) { m_fps->setText( QString::number( fps ) ); } bool NewAnim::getAnimLooping() { return m_loop->isChecked(); } void NewAnim::setAnimLooping( bool loop ) { m_loop->setChecked( loop ); } mm3d-1.3.15/src/implui/newanim.h000066400000000000000000000031351466047437300163560ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __NEWANIM_H #define __NEWANIM_H #include "newanim.base.h" #include "mm3dport.h" #include #include class NewAnim : public QDialog, public Ui::NewAnimBase { Q_OBJECT public: NewAnim( QWidget * parent ); virtual ~NewAnim(); void editMode(); QString getAnimName(); void setAnimName( const QString &name ); bool isSkeletal(); void setSkeletal( bool o ); unsigned getAnimFrameCount(); void setAnimFrameCount( unsigned value ); double getAnimFPS(); void setAnimFPS( double fps ); bool getAnimLooping(); void setAnimLooping( bool loop ); public slots: void nameChangedEvent(); }; #endif // __NEWANIM_H mm3d-1.3.15/src/implui/objprompt.cc000066400000000000000000000054421466047437300170750ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "objprompt.h" #include "objfilter.h" #include "model.h" #include "helpwin.h" #include #include #include ObjPrompt::ObjPrompt() : QDialog( NULL ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } ObjPrompt::~ObjPrompt() { } void ObjPrompt::setOptions( ObjFilter::ObjOptions * opts ) { m_normalsValue->setChecked( opts->m_saveNormals ); m_placesValue->setValue( opts->m_places ); m_texPlacesValue->setValue( opts->m_texPlaces ); m_normalPlacesValue->setValue( opts->m_normalPlaces ); } void ObjPrompt::getOptions( ObjFilter::ObjOptions * opts ) { opts->m_saveNormals = m_normalsValue->isChecked(); opts->m_places = m_placesValue->value(); opts->m_texPlaces = m_texPlacesValue->value(); opts->m_normalPlaces = m_normalPlacesValue->value(); } void ObjPrompt::helpNowEvent() { HelpWin * win = new HelpWin( "olh_objprompt.html", true ); win->show(); } // This function takes a ModelFilter::Options argument, downcasts it // to an ObjOptions object, and uses it to prompt the user for // options for the OBJ file filter. // // If you want to provide options for your format in a plugin // you'll need to call setOptionsPrompt on your filter after you // create it in your plugin_init function. // // This function is registered with the ObjFilter class when the // filter is created in stdfilters.cc. bool objprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ) { bool rval = false; ObjPrompt p; ObjFilter::ObjOptions * opts = dynamic_cast< ObjFilter::ObjOptions * >( o ); if ( opts ) { p.setOptions( opts ); if ( p.exec() ) { rval = true; p.getOptions( opts ); } } else { rval = true; } return rval; } mm3d-1.3.15/src/implui/objprompt.h000066400000000000000000000026471466047437300167430ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __OBJPROMPT_H #define __OBJPROMPT_H #include "objprompt.base.h" #include "modelfilter.h" #include "objfilter.h" #include class Model; class ObjPrompt : public QDialog, public Ui::ObjPromptBase { Q_OBJECT public: ObjPrompt(); virtual ~ObjPrompt(); void setOptions( ObjFilter::ObjOptions * o ); void getOptions( ObjFilter::ObjOptions * o ); public slots: void helpNowEvent(); protected: }; bool objprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ); #endif // __OBJPROMPT_H mm3d-1.3.15/src/implui/offsetwin.cc000066400000000000000000000071231466047437300170630ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "offsetwin.h" #include "helpwin.h" #include "model.h" #include "glmath.h" #include "decalmgr.h" #include "log.h" #include #include #include #include #include OffsetWin::OffsetWin( Model * model, QWidget * parent ) : QDialog( parent, Qt::WindowFlags() ), m_model( model ), m_editing( false ) { setupUi( this ); setModal( true ); setAttribute( Qt::WA_DeleteOnClose ); m_valueSlider->setMinimum( -100 ); m_valueSlider->setMaximum( 100 ); m_valueEdit->setText( QString("0") ); m_rangeEdit->setText( QString("10.00") ); OffsetPosition ov; std::list vertList; m_model->getSelectedVertices( vertList ); std::list::iterator it; for ( it = vertList.begin(); it != vertList.end(); it++ ) { ov.vert = (*it); m_model->getVertexCoords( ov.vert, ov.coords ); m_model->getAverageNormal( ov.vert, ov.normal ); // vertex not connected to a face has no normal if ( ov.normal[0] == 0.0f && ov.normal[1] == 0.0f && ov.normal[2] == 0.0f ) { continue; } m_positions.push_back( ov ); } } OffsetWin::~OffsetWin() { } void OffsetWin::showHelp() { HelpWin * win = new HelpWin( "olh_offsetwin.html", true ); win->show(); } void OffsetWin::valueSliderChanged( int v ) { log_debug( "changed\n" ); if ( v > 100 ) { v = 100; } if ( v < -100 ) { v = -100; } double percent = (double) v / 100.0; double dist = m_range * percent; double coords[3] = { 0.0, 0.0, 0.0 }; OffsetPositionList::iterator it; for ( it = m_positions.begin(); it != m_positions.end(); it++ ) { coords[0] = (*it).coords[0] + (*it).normal[0] * dist; coords[1] = (*it).coords[1] + (*it).normal[1] * dist; coords[2] = (*it).coords[2] + (*it).normal[2] * dist; m_model->moveVertex( (*it).vert, coords[0], coords[1], coords[2] ); } DecalManager::getInstance()->modelUpdated( m_model ); if ( ! m_editing ) { QString str = QString::asprintf( "%d", v ); m_valueEdit->setText( str ); } } void OffsetWin::valueEditChanged( const QString & str ) { m_editing = true; float v = str.toDouble(); m_valueSlider->setValue( (int) v ); m_editing = false; } void OffsetWin::rangeEditChanged( const QString & str ) { m_range = str.toDouble(); valueSliderChanged( m_valueSlider->value() ); } void OffsetWin::accept() { m_model->operationComplete( tr( "Offset by Normal", "operation complete" ).toUtf8() ); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::accept(); } void OffsetWin::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/offsetwin.h000066400000000000000000000036571466047437300167350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __OFFSETWIN_H #define __OFFSETWIN_H #include "offsetwin.base.h" #include "model.h" #include class Model; class OffsetWin : public QDialog, public Ui::OffsetWinBase { Q_OBJECT public: OffsetWin( Model * model, QWidget * parent = NULL ); virtual ~OffsetWin(); public slots: void rangeEditChanged( const QString & ); void valueEditChanged( const QString & ); void valueSliderChanged( int ); void accept(); void reject(); protected: virtual void showHelp(); typedef struct _OffsetPosition_t { int vert; double coords[3]; float normal[3]; } OffsetPosition; typedef std::list OffsetPositionList; Model * m_model; double m_range; OffsetPositionList m_positions; // We don't want to update the edit box if the user is typing in it // So we set this to true when editing. When we update the slider, // our slot will check this value. If false, it will update the edit box bool m_editing; }; #endif // __SPHERIFYWIN_H mm3d-1.3.15/src/implui/painttexturewin.cc000066400000000000000000000160461466047437300203350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "painttexturewin.h" #include "textureframe.h" #include "texwidget.h" #include "model.h" #include "texture.h" #include "log.h" #include "misc.h" #include "3dmprefs.h" #include "msg.h" #include "decalmgr.h" #include "helpwin.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include PaintTextureWin::PaintTextureWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ), m_saved( false ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); m_textureFrame->setModel( model ); m_textureWidget = m_textureFrame->getTextureWidget(); m_textureWidget->setInteractive( false ); m_textureWidget->setMouseOperation( TextureWidget::MouseRange ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); bool foundTexture = false; list triangles; m_model->getSelectedTriangles( triangles ); int material = -1; list::iterator it; for ( it = triangles.begin(); !foundTexture && it != triangles.end(); it++ ) { int g = m_model->getTriangleGroup( *it ); int m = m_model->getGroupTextureId( g ); if ( m >= 0 ) { m_textureFrame->textureChangedEvent( m + 1 ); foundTexture = true; material = m; } } if ( !foundTexture ) { log_error( "no group selected\n" ); } addTriangles( triangles ); m_polygonsButton->setCurrentIndex( 2 ); m_verticesButton->setCurrentIndex( 0 ); m_hSize->setCurrentIndex( 3 ); m_vSize->setCurrentIndex( 3 ); if ( material >= 0 ) { Texture * tex = m_model->getTextureData( material ); if ( tex ) { int x = tex->m_width; int y = tex->m_height; if ( x == y ) { m_hSize->setCurrentIndex( 3 ); m_vSize->setCurrentIndex( 3 ); } else if ( y > x ) { m_hSize->setCurrentIndex( 2 ); m_vSize->setCurrentIndex( 5 ); int index = 2; while ( y > x ) { y = y / 2; index++; } m_vSize->setCurrentIndex( index ); } else { m_vSize->setCurrentIndex( 2 ); m_hSize->setCurrentIndex( 5 ); int index = 2; while ( x > y ) { x = x / 2; index++; } m_hSize->setCurrentIndex( index ); } } } else { m_textureFrame->textureChangedEvent( 0 ); } // TODO allow background, or remove clear button m_clearButton->hide(); m_textureWidget->setSolidBackground( true ); updateDisplay(); } PaintTextureWin::~PaintTextureWin() { } void PaintTextureWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_painttexturewin.html", true ); win->show(); } void PaintTextureWin::accept() { QDialog::accept(); } void PaintTextureWin::textureSizeChangeEvent() { updateDisplay(); } void PaintTextureWin::displayChangedEvent() { updateDisplay(); } void PaintTextureWin::clearEvent() { m_textureWidget->setSolidBackground( true ); m_textureWidget->update(); } void PaintTextureWin::saveEvent() { const char * modelFile = m_model->getFilename(); QString dir = QString::fromUtf8( g_prefs( "ui_model_dir" ).stringValue().c_str() ); if ( modelFile && modelFile[0] != '\0' ) { std::string fullname; std::string fullpath; std::string basename; normalizePath( modelFile, fullname, fullpath, basename ); dir = tr( fullpath.c_str() ); } bool again = true; while ( again ) { again = false; QString filename = QFileDialog::getSaveFileName( this, tr("File name for saved texture?"), dir, QString("PNG Images (*.png *.PNG)") ); if ( filename.length() > 0 ) { bool save = true; if ( file_exists( filename.toUtf8() ) ) { char val = msg_warning_prompt( (const char *) tr( "File exists. Overwrite?" ).toUtf8(), "yNc" ); switch ( val ) { case 'N': again = true; /* fall-through */ case 'C': save = false; break; default: break; } } if ( save ) { int h = atoi( m_hSize->currentText().toLatin1() ); m_textureWidget->update(); QImage img = m_textureWidget->grabFramebuffer(); img = img.scaledToWidth( h, Qt::SmoothTransformation ); if ( !img.save( filename, "PNG", 100 ) ) { QString msg = tr( "Could not write file: " ) + QString( "\n" ); msg += filename; msg_error( (const char *) msg.toUtf8() ); } updateDisplay(); } } else { log_debug( "save frame buffer cancelled\n" ); } } } void PaintTextureWin::addTriangles( const list & triangles ) { m_textureWidget->clearCoordinates(); list::const_iterator it; float u = 0.0f; float v = 0.0f; for( it = triangles.begin(); it != triangles.end(); it++ ) { int t; int vert[3]; for ( t = 0; t < 3; t++ ) { m_model->getTextureCoords( (*it), t, u, v ); vert[t] = m_textureWidget->addVertex( u, v ); } m_textureWidget->addTriangle( vert[0], vert[1], vert[2] ); } } void PaintTextureWin::updateDisplay() { int dm = m_polygonsButton->currentIndex(); dm++; m_textureWidget->setDrawMode( static_cast(dm) ); m_textureWidget->setDrawVertices( (m_verticesButton->currentIndex() != 0) ? true : false ); m_textureFrame->sizeOverride( atoi( m_hSize->currentText().toLatin1() ), atoi( m_vSize->currentText().toLatin1() ) ); m_textureWidget->update(); } mm3d-1.3.15/src/implui/painttexturewin.h000066400000000000000000000032471466047437300201760ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __PAINTTEXTUREWIN_H #define __PAINTTEXTUREWIN_H #include #include "painttexturewin.base.h" #include #include using std::list; using std::map; #include class Model; class TextureWidget; class PaintTextureWin : public QDialog, public Ui::PaintTextureWinBase { Q_OBJECT public: PaintTextureWin( Model * model, QWidget * parent = NULL ); ~PaintTextureWin(); public slots: void helpNowEvent(); void textureSizeChangeEvent(); void displayChangedEvent(); void clearEvent(); void saveEvent(); void accept(); protected: void updateDisplay(); void addTriangles( const list & triangles ); TextureWidget * m_textureWidget; Model * m_model; bool m_saved; }; #endif // __PAINTTEXTUREWIN_H mm3d-1.3.15/src/implui/paintwidget.cc000066400000000000000000000043641466047437300174020ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "paintwidget.h" #include "texwidget.h" #include "log.h" PaintWidget::PaintWidget( TextureWidget * drawBuddy, QWidget * parent ) : QOpenGLWidget( parent ), m_drawBuddy( drawBuddy ) { } PaintWidget::~PaintWidget() { } void PaintWidget::initializeGL() { if ( !isValid() ) { log_error( "paint widget does not have a valid OpenGL context\n" ); return; } glEnable( GL_TEXTURE_2D ); glShadeModel( GL_SMOOTH ); glDepthFunc( GL_LEQUAL ); glClearColor( 0.0, 0.0, 0.0, 1.0 ); glClearDepth( 1.0f ); GLfloat ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat position[] = { 0.0f, 0.0f, 3.0f, 0.0f }; glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE ); glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); glLightfv( GL_LIGHT0, GL_POSITION, position ); glDisable( GL_LIGHT0 ); glDisable( GL_LIGHTING ); } void PaintWidget::resizeGL( int w, int h ) { glViewport( 0, 0, ( GLint ) w, ( GLint ) h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); glOrtho( 0.0, 1.0, 0.0, 1.0, -1.0, 1.0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity( ); } void PaintWidget::paintGL() { // I don't draw on myself log_debug( "PaintWidget::paintGL\n" ); m_drawBuddy->paintOnGlWidget( this ); } mm3d-1.3.15/src/implui/paintwidget.h000066400000000000000000000025121466047437300172350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __PAINTWIDGET_H #define __PAINTWIDGET_H #include #include #include class TextureWidget; class PaintWidget : public QOpenGLWidget { public: PaintWidget( TextureWidget * drawBuddy, QWidget * parent = NULL ); virtual ~PaintWidget(); protected: void initializeGL() override; void paintGL() override; void resizeGL( int w, int h ) override; TextureWidget * m_drawBuddy; }; #endif // __PAINTWIDGET_H mm3d-1.3.15/src/implui/pluginwin.cc000066400000000000000000000055741466047437300171030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "pluginmgr.h" #include "pluginwin.h" #include "helpwin.h" #include #include PluginWindow::PluginWindow() : QDialog( NULL ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( false ); m_pluginList->header()->setSectionsClickable( false ); m_pluginList->header()->setSectionsMovable( false ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); refreshPluginData(); } PluginWindow::~PluginWindow() { } void PluginWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_pluginwin.html", true ); win->show(); } void PluginWindow::refreshPluginData() { PluginManager * pmgr = PluginManager::getInstance(); list plist = pmgr->getPluginIds(); list::iterator it; for ( it = plist.begin(); it != plist.end(); it++ ) { QTreeWidgetItem * item = new QTreeWidgetItem( m_pluginList ); item->setText( 0, QString( pmgr->getPluginName( *it ) ) + " " ); item->setText( 1, QString( pmgr->getPluginVersion( *it ) ) + " " ); item->setText( 2, QString( pmgr->getPluginDescription( *it ) ) + " " ); const char * status = "Unknown"; switch ( pmgr->getPluginStatus( *it ) ) { case PluginManager::PluginActive: status = "Active"; break; case PluginManager::PluginUserDisabled: status = "Disabled by user"; break; case PluginManager::PluginVersionDisabled: status = "Disabled (incompatible version)"; break; case PluginManager::PluginNotPlugin: status = "Not a plugin"; break; case PluginManager::PluginError: status = "Error"; break; default: status = "Unknown status code"; break; } item->setText( 3, QString(status) + " " ); item->setText( 4, QString( pmgr->getPluginFilename( *it ) ) + " " ); } } mm3d-1.3.15/src/implui/pluginwin.h000066400000000000000000000023131466047437300167310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __PLUGINWIN_H #define __PLUGINWIN_H #include "pluginwin.base.h" #include class PluginWindow : public QDialog, public Ui::PluginWinBase { Q_OBJECT public: PluginWindow(); virtual ~PluginWindow(); public slots: void helpNowEvent(); protected: void refreshPluginData(); }; #endif // __PLUGINWIN_H mm3d-1.3.15/src/implui/pointwin.cc000066400000000000000000000100731466047437300167240ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "pointwin.h" #include "decalmgr.h" #include "helpwin.h" #include #include #include #include #include using std::list; #include "model.h" #include "decalmgr.h" #include "log.h" #include "msg.h" #include "modelstatus.h" PointWin::PointWin( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); int t; for ( t = 0; t < m_model->getPointCount(); t++ ) { m_pointName->insertItem( t, QString::fromUtf8( m_model->getPointName(t) ) ); } for ( t = 0; t < m_model->getBoneJointCount(); t++ ) { m_pointJoint->insertItem( t + 1, QString::fromUtf8( m_model->getBoneJointName(t) ) ); } list points; m_model->getSelectedPoints( points ); if ( ! points.empty() ) { m_pointName->setCurrentIndex( points.front() ); pointNameSelected( points.front() ); } else { m_pointName->setCurrentIndex( 0 ); pointNameSelected( 0 ); } } PointWin::~PointWin() { } void PointWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_pointwin.html", true ); win->show(); } void PointWin::pointNameSelected( int index ) { if ( index < m_pointName->count() ) { m_model->unselectAllPoints(); m_model->selectPoint( index ); m_deleteButton->setEnabled( true ); m_renameButton->setEnabled( true ); m_pointJoint->setEnabled( true ); m_pointJoint->setCurrentIndex( m_model->getPointBoneJoint( index ) + 1 ); DecalManager::getInstance()->modelUpdated( m_model ); } else { m_deleteButton->setEnabled( false ); m_renameButton->setEnabled( false ); m_pointJoint->setEnabled( false ); m_pointJoint->setCurrentIndex( 0 ); } } void PointWin::pointJointSelected( int index ) { if ( m_pointName->count() > 0 ) { if ( index >= 0 && index < m_pointJoint->count() ) { m_model->setPointBoneJoint( m_pointName->currentIndex(), index - 1 ); } } } void PointWin::deleteClicked() { if ( m_pointName->count() ) { m_model->deletePoint( m_pointName->currentIndex() ); } } void PointWin::renameClicked() { if ( m_pointName->count() ) { bool ok = false; int pointNum = m_pointName->currentIndex(); QString pointName = QInputDialog::getText( this, tr("Rename point", "window title"), tr("Enter new point name:"), QLineEdit::Normal, QString::fromUtf8( m_model->getPointName( pointNum )), &ok ); if ( ok ) { m_model->setPointName( pointNum, pointName.toUtf8() ); m_pointName->setItemText( pointNum, pointName ); } } } void PointWin::accept() { log_debug( "Point changes complete\n" ); m_model->operationComplete( tr( "Point changes", "operation complete" ).toUtf8() ); QDialog::accept(); } void PointWin::reject() { log_debug( "Point changes canceled\n" ); m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } mm3d-1.3.15/src/implui/pointwin.h000066400000000000000000000027361466047437300165750ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __POINTWIN_H #define __POINTWIN_H #include "pointwin.base.h" #include class Model; class Q3Accel; class PointWin : public QDialog, public Ui::PointWinBase { Q_OBJECT public: PointWin( Model * model, QWidget * parent = NULL ); virtual ~PointWin(); public slots: void helpNowEvent(); void deleteClicked(); void renameClicked(); void pointNameSelected( int index ); void pointJointSelected( int index ); protected slots: void accept(); void reject(); protected: Q3Accel * m_accel; Model * m_model; }; #endif // __POINTWIN_H mm3d-1.3.15/src/implui/projectionwin.cc000066400000000000000000000313531466047437300177530ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "projectionwin.h" #include "decalmgr.h" #include "helpwin.h" #include "textureframe.h" #include "texwidget.h" #include "decalmgr.h" #include #include #include #include #include #include #include using std::list; #include "model.h" #include "decalmgr.h" #include "log.h" #include "msg.h" #include "modelstatus.h" ProjectionWin::ProjectionWin( Model * model, QWidget * parent, ViewPanel * viewPanel ) : QDialog( parent ), m_viewPanel( viewPanel ), m_undoCount( 0 ), m_redoCount( 0 ), m_inUndo( false ), m_ignoreChange( false ) { setupUi( this ); m_material->hide(); m_materialLabel->hide(); m_textureWidget = m_textureFrame->getTextureWidget(); m_textureWidget->setInteractive( true ); m_textureWidget->setMouseOperation( TextureWidget::MouseRange ); m_textureWidget->setDrawVertices( false ); m_textureWidget->setMouseTracking( true ); connect( m_textureWidget, SIGNAL(updateRangeSignal()), this, SLOT(rangeChangedEvent()) ); connect( m_textureWidget, SIGNAL(updateRangeDoneSignal()), this, SLOT(applyProjectionEvent()) ); connect( m_textureWidget, SIGNAL(updateSeamSignal(double,double)), this, SLOT(seamChangedEvent(double,double)) ); connect( m_textureWidget, SIGNAL(updateSeamDoneSignal()), this, SLOT(applyProjectionEvent()) ); connect( m_textureWidget, SIGNAL(zoomLevelChanged(QString)), this, SLOT(zoomLevelChangedEvent(QString)) ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); QShortcut * undo = new QShortcut( QKeySequence( tr("CTRL+Z", "Undo shortcut")), this ); connect( undo, SIGNAL(activated()), this, SLOT(undoEvent()) ); QShortcut * redo = new QShortcut( QKeySequence( tr("CTRL+Y", "Redo shortcut")), this ); connect( redo, SIGNAL(activated()), this, SLOT(redoEvent()) ); // can't do this until constructor is done (because of Model::Observer interface) //setModel( model ); } ProjectionWin::~ProjectionWin() { } void ProjectionWin::refreshProjectionDisplay() { m_textureWidget->clearCoordinates(); if ( m_projection->count() > 0 ) { int proj = m_projection->currentIndex(); unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { int p = m_model->getTriangleProjection( t ); if ( p == proj ) { int g = m_model->getTriangleGroup( t ); if ( g >= 0 ) { int material = m_model->getGroupTextureId( g ); if ( material >= 0 ) { m_textureFrame->textureChangedEvent( material + 1 ); // TODO bah, off-by-one, I need to fix this addProjectionTriangles(); return; } } } } } m_textureWidget->update(); m_textureFrame->textureChangedEvent( -1 ); DecalManager::getInstance()->modelUpdated( m_model ); } void ProjectionWin::modelChanged( int changeBits ) { if ( !m_ignoreChange ) { if ( !m_inUndo ) { m_undoCount = 0; m_redoCount = 0; } // TODO need some way to re-select the projection we were looking at if ( isVisible() ) { int projCount = m_model->getProjectionCount(); if ( projCount != m_projection->count() ) { // A projection was added or deleted, we need to select a new // projection and re-initialize everything. initWindow(); } else { // a change to projection itself, or a non-projection change, just // re-initialize the projection display for the current projection int p = m_projection->currentIndex(); m_projection->setItemText( p, QString::fromUtf8( m_model->getProjectionName( p )) ); m_type->setCurrentIndex( m_model->getProjectionType( p ) ); // TODO material/test pattern? addProjectionTriangles(); } } } } void ProjectionWin::addProjectionTriangles() { m_textureWidget->clearCoordinates(); int p = getSelectedProjection(); double uv[4] = { 0, 0, 0, 0 }; m_model->getProjectionRange( p, uv[0], uv[1], uv[2], uv[3] ); m_textureWidget->setRange( uv[0], uv[1], uv[2], uv[3] ); unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->getTriangleProjection( t ) == p ) { int verts[3] = { 0, 0, 0 }; for ( int i = 0; i < 3; i++ ) { float u = 1.0; float v = 1.0; m_model->getTextureCoords( t, i, u, v ); verts[i] = m_textureWidget->addVertex( u, v ); } m_textureWidget->addTriangle( verts[0], verts[1], verts[2] ); } } m_textureWidget->update(); DecalManager::getInstance()->modelUpdated( m_model ); } void ProjectionWin::setModel( Model * model ) { m_undoCount = 0; m_redoCount = 0; if ( model != m_model ) { model->addObserver( this ); } m_model = model; m_textureFrame->setModel( model ); if ( isVisible() ) { initWindow(); } } void ProjectionWin::helpNowEvent() { HelpWin * win = new HelpWin( "olh_projectionwin.html", true ); win->show(); } void ProjectionWin::undoEvent() { if ( m_undoCount > 0 ) { m_inUndo = true; m_model->undo(); m_undoCount--; m_inUndo = false; } } void ProjectionWin::redoEvent() { if ( m_undoCount < m_redoCount ) { m_inUndo = true; m_model->redo(); m_undoCount++; m_inUndo = false; } } void ProjectionWin::show() { setModel( m_model ); if ( !isVisible() ) { // If we are visible, setModel already did this initWindow(); } QDialog::show(); } void ProjectionWin::initWindow() { m_projection->clear(); if ( m_model ) { unsigned pcount = m_model->getProjectionCount(); bool enabled = true; if ( pcount == 0 ) { enabled = false; } m_type->setEnabled( enabled ); m_material->setEnabled( enabled ); m_textureFrame->setEnabled( enabled ); m_addFacesButton->setEnabled( enabled ); m_removeFacesButton->setEnabled( enabled ); m_renameButton->setEnabled( enabled ); m_zoomInput->setEnabled( enabled ); m_zoomInButton->setEnabled( enabled ); m_zoomOutButton->setEnabled( enabled ); m_applyButton->setEnabled( enabled ); m_resetButton->setEnabled( enabled ); if ( enabled ) { for ( unsigned p = 0; p < pcount; p++ ) { m_projection->insertItem( p, QString::fromUtf8( m_model->getProjectionName(p) ) ); } bool found = false; for ( unsigned p = 0; p < pcount; p++ ) { if ( m_model->isProjectionSelected( p ) ) { found = true; m_projection->setCurrentIndex( p ); projectionIndexChangedEvent( p ); break; } } if ( !found ) { unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { int p = m_model->getTriangleProjection( t ); if ( p >= 0 ) { m_projection->setCurrentIndex( p ); projectionIndexChangedEvent( p ); break; } } } } } } } void ProjectionWin::closeEvent( QCloseEvent * e ) { e->ignore(); hide(); } void ProjectionWin::zoomIn() { m_textureWidget->zoomIn(); } void ProjectionWin::zoomOut() { m_textureWidget->zoomOut(); } void ProjectionWin::typeChangedEvent( int type ) { int p = getSelectedProjection(); if ( p >= 0 ) { m_model->setProjectionType( p, type ); //m_model->applyProjection( p ); operationComplete( tr( "Set Projection Type", "operation complete" ).toUtf8() ); } refreshProjectionDisplay(); } void ProjectionWin::addFacesEvent() { int p = getSelectedProjection(); unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { m_model->setTriangleProjection( t, p ); } } m_model->applyProjection( p ); operationComplete( tr( "Set Triangle Projection", "operation complete" ).toUtf8() ); refreshProjectionDisplay(); } void ProjectionWin::removeFacesEvent() { unsigned tcount = m_model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( m_model->isTriangleSelected( t ) ) { m_model->setTriangleProjection( t, -1 ); } } operationComplete( tr( "Set Triangle Projection", "operation complete" ).toUtf8() ); refreshProjectionDisplay(); } void ProjectionWin::applyProjection() { int p = getSelectedProjection(); if ( p >= 0 ) { m_model->applyProjection( p ); } addProjectionTriangles(); } void ProjectionWin::applyProjectionEvent() { applyProjection(); operationComplete( tr("Apply Projection", "operation complete").toUtf8() ); } void ProjectionWin::resetClickedEvent() { int p = getSelectedProjection(); m_model->setProjectionRange( p, 0.0, 0.0, 1.0, 1.0 ); operationComplete( tr( "Reset UV Coordinates", "operation complete" ).toUtf8() ); addProjectionTriangles(); //applyProjectionEvent(); } void ProjectionWin::renameClickedEvent() { int p = getSelectedProjection(); if ( p >= 0 ) { bool ok = false; QString projName = QInputDialog::getText( this, tr("Rename projection", "window title"), tr("Enter new point name:"), QLineEdit::Normal, QString::fromUtf8( m_model->getProjectionName( p )), &ok ); if ( ok ) { m_model->setProjectionName( p, projName.toUtf8() ); m_projection->setItemText( p, projName ); operationComplete( tr( "Rename Projection", "operation complete" ).toUtf8() ); } } } void ProjectionWin::projectionIndexChangedEvent( int newIndex ) { int type = m_model->getProjectionType( newIndex ); m_type->setCurrentIndex( type ); double uv[4] = { 0, 0, 0, 0 }; m_model->getProjectionRange( newIndex, uv[0], uv[1], uv[2], uv[3] ); m_textureWidget->setRange( uv[0], uv[1], uv[2], uv[3] ); refreshProjectionDisplay(); } void ProjectionWin::zoomChangeEvent() { double zoom = m_zoomInput->text().toDouble(); if ( zoom < 0.00001 ) { zoom = 1; } m_textureWidget->setZoomLevel( zoom ); } void ProjectionWin::zoomLevelChangedEvent( QString zoomStr ) { m_zoomInput->setText( zoomStr ); } void ProjectionWin::rangeChangedEvent() { int p = getSelectedProjection(); double uv[4] = { 0, 0, 0, 0 }; m_textureWidget->getRange( uv[0], uv[1], uv[2], uv[3] ); m_model->setProjectionRange( p, uv[0], uv[1], uv[2], uv[3] ); addProjectionTriangles(); } void ProjectionWin::seamChangedEvent( double xDiff, double yDiff ) { if ( fabs( xDiff ) > 0.0 ) { int p = getSelectedProjection(); double up[4] = { 0, 0, 0, 1 }; double seam[4] = { 0, 0, 0, 1 }; m_model->getProjectionUp( p, up ); m_model->getProjectionSeam( p, seam ); Matrix m; m.setRotationOnAxis( up, xDiff ); m.apply3( seam ); m_model->setProjectionSeam( p, seam ); addProjectionTriangles(); } } int ProjectionWin::getSelectedProjection() { if ( m_projection->count() > 0 ) { return m_projection->currentIndex(); } return -1; } void ProjectionWin::operationComplete( const char * opname ) { m_undoCount++; m_redoCount = m_undoCount; m_ignoreChange = true; m_model->operationComplete( opname ); m_ignoreChange = false; } mm3d-1.3.15/src/implui/projectionwin.h000066400000000000000000000046751466047437300176240ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __POINTWIN_H #define __POINTWIN_H #include "projectionwin.base.h" #include "model.h" #include #include class ViewPanel; class TextureWidget; class ProjectionWin : public QDialog, public Ui::ProjectionWinBase, public Model::Observer { Q_OBJECT public: ProjectionWin( Model * model, QWidget * parent, ViewPanel * viewPanel ); virtual ~ProjectionWin(); void refreshProjectionDisplay(); void addProjectionTriangles(); // Model::Observer methods void modelChanged( int changeBits ); public slots: void show(); void setModel( Model * m ); void zoomIn(); void zoomOut(); void undoEvent(); void redoEvent(); void helpNowEvent(); protected slots: void closeEvent( QCloseEvent * e ); void typeChangedEvent(int); void addFacesEvent(); void removeFacesEvent(); void applyProjectionEvent(); void resetClickedEvent(); void renameClickedEvent(); void projectionIndexChangedEvent(int); void zoomChangeEvent(); void zoomLevelChangedEvent( QString zoomStr ); void rangeChangedEvent(); void seamChangedEvent( double xDiff, double yDiff ); protected: void initWindow(); void applyProjection(); int getSelectedProjection(); void operationComplete( const char * opname ); Model * m_model; ViewPanel * m_viewPanel; TextureWidget * m_textureWidget; int m_undoCount; int m_redoCount; bool m_inUndo; bool m_ignoreChange; }; #endif // __POINTWIN_H mm3d-1.3.15/src/implui/qtmain.cc000066400000000000000000000162421466047437300163520ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "qtmain.h" #include "viewwin.h" #include "model.h" #include "sysconf.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "mm3dport.h" #include "misc.h" #include "msg.h" #include "msgqt.h" #include "3dmprefs.h" #include "cmdline.h" #include "mlocale.h" // Handle macOS opening file in finder or dropping file on dock icon. // File types must be listed in AppBundle.app/Contents/Info.plist. class ModelApp : public QApplication { public: ModelApp(int &argc, char **argv) : QApplication(argc, argv), m_darkMode(false) { checkDarkMode(); } bool event(QEvent *event) { if ( event->type() == QEvent::FileOpen ) { QFileOpenEvent *openEvent = static_cast(event); ViewWindow::openModelInEmptyWindow( openEvent->file().toUtf8() ); } else if ( event->type() == QEvent::ApplicationPaletteChange ) { checkDarkMode(); } return QApplication::event(event); } void checkDarkMode() { m_darkMode = this->palette().window().color().value() < this->palette().windowText().color().value(); } bool getDarkMode() { return m_darkMode; } protected: bool m_darkMode; }; static ModelApp * s_app = NULL; static QTranslator * s_qtXlat = NULL; static QTranslator * s_mm3dXlat = NULL; static void _cleanup() { // TODO add any necessary cleanup } static bool loadTranslationFile( QTranslator * xlat, const QString & localeFile ) { std::list path_list; path_list.push_back( "." ); path_list.push_back( "../translations" ); path_list.push_back( getTranslationsDirectory() ); path_list.push_back( QLibraryInfo::location( QLibraryInfo::TranslationsPath ).toUtf8().data() ); // try current directory first (for override), then mm3d system directory for ( std::list::iterator it = path_list.begin(); it != path_list.end(); ++it ) { log_debug( "attempting to load translation %s from %s\n", (const char *) localeFile.toUtf8(), (const char *) it->c_str() ); if ( xlat->load( localeFile, it->c_str() ) ) { log_debug( " loaded.\n" ); return true; } } log_warning( "unable to load translation for %s\n", (const char *) localeFile.toUtf8() ); return false; } QApplication * ui_getapp() { return s_app; } bool ui_getdarkmode() { return ( s_app && s_app->getDarkMode() ); } int ui_prep( int & argc, char * argv[] ) { #if defined Q_OS_UNIX && !defined Q_OS_MAC // Don't use Wayland unless requested (QT_QPA_PLATFORM=wayland) char *platform = getenv( "QT_QPA_PLATFORM" ); if ( !platform || !*platform ) { setenv( "QT_QPA_PLATFORM", "xcb", 1 ); } #endif // Don't use OpenGL ES (ANGLE, software) on Windows // (It must be set before QApplication is created) QCoreApplication::setAttribute( Qt::AA_UseDesktopOpenGL ); QCoreApplication::setAttribute( Qt::AA_EnableHighDpiScaling ); s_app = new ModelApp( argc, argv ); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) // Disable "?" button (What's this?) on Windows QDialogs s_app->setAttribute( Qt::AA_DisableWindowContextHelpButton ); #endif #ifdef Q_OS_MAC // Don't use native mac menu bar as it's misleading (the menu affects // minimized window when it should not). This could be fixed by using // a separate menu when no window is active but I've failed to get // that to work correct with Qt 5.11.1. s_app->setAttribute( Qt::AA_DontUseNativeMenuBar ); #endif // Set default format for QOpenGLWidget. QSurfaceFormat format = QSurfaceFormat::defaultFormat(); format.setRenderableType( QSurfaceFormat::OpenGL ); format.setVersion( 1, 2 ); format.setSwapBehavior( QSurfaceFormat::DoubleBuffer ); QSurfaceFormat::setDefaultFormat( format ); QString loc = mlocale_get().c_str(); if ( loc == "" ) { loc = QLocale::system().name(); } // General Qt translations s_qtXlat = new QTranslator( 0 ); QString qtLocale = QString( "qt_" ) + loc; loadTranslationFile( s_qtXlat, qtLocale ); s_app->installTranslator( s_qtXlat ); // MM3D translations s_mm3dXlat = new QTranslator( 0 ); QString mm3dLocale = QString( "mm3d_" ) + loc; loadTranslationFile( s_mm3dXlat, mm3dLocale ); s_app->installTranslator( s_mm3dXlat ); return 0; } int ui_init( int & argc, char * argv[] ) { int rval = 0; if ( !s_app ) { ui_prep( argc, argv ); } if ( cmdline_runcommand ) { rval = cmdline_command(); } if ( cmdline_runui ) { bool opened = false; unsigned openCount = 0; init_msgqt(); openCount = cmdline_getOpenModelCount(); if ( openCount == 0 ) { char * pwd = ( argc > 1 ) ? PORT_get_current_dir_name() : NULL; for ( int t = 1; t < argc; t++ ) { std::string file = normalizePath( argv[t], pwd ); if ( ViewWindow::openModel( file.c_str() ) ) { openCount++; opened = true; } } if ( pwd ) { free( pwd ); } } else { for ( unsigned t = 0; t < openCount; t++ ) { Model * m = cmdline_getOpenModel( t ); ViewWindow * win = new ViewWindow( m ); win->getSaved(); // Just so I don't have a warning opened = true; } cmdline_clearOpenModelList(); } if ( opened ) { rval = s_app->exec(); } else { ViewWindow * win = new ViewWindow( new Model ); win->getSaved(); // Just so I don't have a warning rval = s_app->exec(); /* StartPrompt p; p.exec(); if ( !p.shouldExit() ) { rval = s_app->exec(); } */ } _cleanup(); } delete s_mm3dXlat; delete s_qtXlat; delete s_app; s_app = NULL; s_qtXlat = NULL; s_mm3dXlat = NULL; return rval; } void ui_exit() { _cleanup(); if ( qApp ) { qApp->quit(); } else { exit( 0 ); } } mm3d-1.3.15/src/implui/qtmain.h000066400000000000000000000021521466047437300162070ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __QTMAIN_H #define __QTMAIN_H class QApplication; extern QApplication * ui_getapp(); extern bool ui_getdarkmode(); extern int ui_prep( int & argc, char * argv[] ); extern int ui_init( int & argc, char * argv[] ); extern void ui_exit(); #endif // __QTMAIN_H mm3d-1.3.15/src/implui/qttex.cc000066400000000000000000000270711466047437300162300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "qttex.h" //Added by qt3to4: #include "mm3dconfig.h" #include "log.h" #include #include #include #include #include #include #include #include using std::list; using std::string; QtTextureFilter::QtTextureFilter() : m_initialized( false ) { } QtTextureFilter::~QtTextureFilter() { } bool QtTextureFilter::canRead( const char * filename ) { initializeSupported(); if ( filename == NULL ) { return false; } QString cmpstr; unsigned len = strlen( filename ); QStringList::Iterator it; for ( it = m_read.begin(); it != m_read.end(); it++ ) { cmpstr = QString(".") + *it; if ( (int) len >= (int) cmpstr.length() ) { if ( strcasecmp( &filename[len-cmpstr.length()], (const char *) cmpstr.toUtf8()) == 0 ) { return true; } } } return false; } bool QtTextureFilter::canWrite( const char * filename ) { initializeSupported(); if ( filename == NULL ) { return false; } QString cmpstr; unsigned len = strlen( filename ); QStringList::Iterator it; for ( it = m_write.begin(); it != m_write.end(); it++ ) { cmpstr = QString(".") + *it; if ( (int) len >= (int) cmpstr.length() ) { if ( strcasecmp( &filename[len-cmpstr.length()], (const char *) cmpstr.toUtf8()) == 0 ) { return true; } } } return false; } Texture::ErrorE QtTextureFilter::readFile( Texture * texture, const char * filename ) { initializeSupported(); QImage image; if ( filename == NULL ) { log_error( "filename NULL\n" ); return Texture::ERROR_NO_FILE; } if ( texture == NULL ) { log_error( "texture NULL\n" ); return Texture::ERROR_UNKNOWN; } if ( image.load( QString::fromUtf8( filename ) ) ) { imageToTexture( texture, &image ); texture->m_filename = strdup( filename ); const char * name = strrchr( filename, DIR_SLASH ); if ( name ) { texture->m_name = strdup( &name[1] ); } else { texture->m_name = strdup( filename ); } char * ext = strrchr( texture->m_name, '.' ); if ( ext ) { ext[0] = '\0'; } return Texture::ERROR_NONE; } else { return Texture::ERROR_FILE_OPEN; } return Texture::ERROR_UNKNOWN; } Texture::ErrorE QtTextureFilter::readMemory( const char * format, Texture * texture, const ImageData * d ) { initializeSupported(); if ( format == NULL || texture == NULL || d == NULL ) { log_error( "bad argument\n" ); return Texture::ERROR_UNKNOWN; } char fmt[5] = "PNG"; getFormatString( fmt, format ); log_debug( "loading image from data for %s, size %" PORTuSIZE "\n", fmt, d->getDataSize() ); const uint8_t * ptr = d->getConstDataPtr(); if ( ptr == NULL ) { log_error( "ImageData data pointer is null\n" ); return Texture::ERROR_UNKNOWN; } QImage image; if ( image.loadFromData( ptr, d->getDataSize() ) ) { imageToTexture( texture, &image ); texture->m_filename = (char *) malloc( 20 ); sprintf( texture->m_filename, "%p", texture ); texture->m_name = (char *) malloc( 20 ); strcpy( texture->m_name, texture->m_filename ); return Texture::ERROR_NONE; } else { log_debug( "load failed\n" ); return Texture::ERROR_FILE_READ; } return Texture::ERROR_UNKNOWN; } Texture::ErrorE QtTextureFilter::writeFile( Texture * texture, const char * filename ) { initializeSupported(); if ( filename == NULL ) { log_error( "filename NULL\n" ); return Texture::ERROR_NO_FILE; } if ( texture == NULL ) { log_error( "texture NULL\n" ); return Texture::ERROR_UNKNOWN; } uint8_t * data = new uint8_t[ texture->m_width * texture->m_height * 4 ]; textureToImage( texture, data ); char fmt[5] = "PNG"; getFormatString( fmt, filename ); Texture::ErrorE err = Texture::ERROR_NONE; { QImage image ( data, texture->m_width, texture->m_height, QImage::Format_ARGB32 ); if ( ! image.save( QString::fromUtf8( filename ), fmt, 100 ) ) { return Texture::ERROR_FILE_WRITE; } } delete[] data; // Must do this after image goes out of scope return err; } Texture::ErrorE QtTextureFilter::writeMemory( const char * format, Texture * texture, TextureFilter::ImageData ** d ) { initializeSupported(); if ( format == NULL ) { log_error( "filename NULL\n" ); return Texture::ERROR_NO_FILE; } if ( texture == NULL || d == NULL ) { log_error( "texture NULL\n" ); return Texture::ERROR_UNKNOWN; } uint8_t * data = new uint8_t[ texture->m_width * texture->m_height * 4 ]; textureToImage( texture, data ); char fmt[5] = "PNG"; getFormatString( fmt, format ); Texture::ErrorE err = Texture::ERROR_NONE; { QImage image ( data, texture->m_width, texture->m_height, QImage::Format_ARGB32 ); QByteArray ba; QBuffer buffer( &ba ); buffer.open( QIODevice::WriteOnly ); if ( ! image.save( &buffer, fmt, 100 ) ) { return Texture::ERROR_FILE_WRITE; } *d = TextureFilter::ImageData::get( ba.size() ); memcpy( (*d)->getDataPtr(), ba.data(), ba.size() ); } delete[] data; // Must do this after image goes out of scope return err; } list QtTextureFilter::getReadTypes() { initializeSupported(); list rval; QStringList::Iterator it; for ( it = m_read.begin(); it != m_read.end(); it++ ) { rval.push_back( (const char *) (QString("*.") + *it).toUtf8() ); } return rval; } list QtTextureFilter::getWriteTypes() { initializeSupported(); list rval; QStringList::Iterator it; for ( it = m_write.begin(); it != m_write.end(); it++ ) { rval.push_back( (const char *) (QString("*.") + *it).toUtf8() ); } return rval; } //------------------------------------------------------------------ // Protected members //------------------------------------------------------------------ void QtTextureFilter::getFormatString( char * format, const char * filename ) { const char * ext = strrchr( filename, '.' ); if ( ext ) { ext++; // Skip '.' } else { ext = (char *) filename; } strncpy( format, ext, 5 ); for ( unsigned t = 0; format[t]; t++ ) { format[t] = toupper( format[t] ); } if ( strcmp( format, "JPG" ) == 0 ) { strcpy( format, "JPEG" ); } if ( strcmp( format, "TIF" ) == 0 ) { strcpy( format, "TIFF" ); } } void QtTextureFilter::textureToImage( Texture * texture, uint8_t * data ) { unsigned sbpp = ( texture->m_format == Texture::FORMAT_RGB ) ? 3 : 4; unsigned dbpp = 4; uint8_t * src; uint8_t * dest; for ( int y = 0; y < texture->m_height; y++ ) { for ( int x = 0; x < texture->m_width; x++ ) { src = &texture->m_data[ ((texture->m_height - y - 1) * texture->m_width + x) * sbpp ]; dest = &data[ (y * texture->m_width + x) * dbpp ]; dest[ 0 ] = src[ 2 ]; dest[ 1 ] = src[ 1 ]; dest[ 2 ] = src[ 0 ]; if ( texture->m_format == Texture::FORMAT_RGBA ) { dest[ 3 ] = src[3]; } else { dest[ 3 ] = 0xff; } } } } void QtTextureFilter::imageToTexture( Texture * texture, QImage * image ) { texture->m_width = image->width(); texture->m_height = image->height(); bool hasAlpha = image->hasAlphaChannel(); log_debug( "Alpha channel: %s\n", hasAlpha ? "present" : "not present" ); // Ignore opaque alpha channel if ( hasAlpha ) { hasAlpha = false; for ( int y = 0; y < texture->m_height && hasAlpha == false; y ++ ) { for ( int x = 0; x < texture->m_width && hasAlpha == false; x++ ) { QRgb p = image->pixel( x, texture->m_height - y - 1 ); if ( qAlpha( p ) < 255 ) { hasAlpha = true; } } } } unsigned pixelBytes = hasAlpha ? 4 : 3; unsigned pixelCount = texture->m_width * texture->m_height; unsigned imageSize = pixelCount * (pixelBytes * sizeof(uint8_t)); texture->m_data = new uint8_t[ imageSize ]; // Make bottom row the first row, as required by OpenGL if ( hasAlpha ) { texture->m_format = Texture::FORMAT_RGBA; for ( int y = 0; y < texture->m_height; y ++ ) { for ( int x = 0; x < texture->m_width; x++ ) { QRgb p = image->pixel( x, texture->m_height - y - 1 ); texture->m_data[ ((y * texture->m_width + x)*4) + 0 ] = qRed( p ); texture->m_data[ ((y * texture->m_width + x)*4) + 1 ] = qGreen( p ); texture->m_data[ ((y * texture->m_width + x)*4) + 2 ] = qBlue( p ); texture->m_data[ ((y * texture->m_width + x)*4) + 3 ] = qAlpha( p ); } } } else { texture->m_format = Texture::FORMAT_RGB; for ( int y = 0; y < texture->m_height; y ++ ) { for ( int x = 0; x < texture->m_width; x++ ) { QRgb p = image->pixel( x, texture->m_height - y - 1 ); texture->m_data[ ((y * texture->m_width + x)*3) + 0 ] = qRed( p ); texture->m_data[ ((y * texture->m_width + x)*3) + 1 ] = qGreen( p ); texture->m_data[ ((y * texture->m_width + x)*3) + 2 ] = qBlue( p ); } } } } void QtTextureFilter::initializeSupported() { if ( !m_initialized ) { m_initialized = true; { QList< QByteArray > list = QImageReader::supportedImageFormats(); for ( QList< QByteArray >::iterator it = list.begin(); it != list.end(); it++ ) { const char * str = (*it).constData(); m_read.push_back( QString(str) ); if ( strcasecmp( str, "JPEG" ) == 0 ) { m_read.push_back( QString("JPG") ); } if ( strcasecmp( str, "TIFF" ) == 0 ) { m_read.push_back( QString("TIF") ); } } } { QList< QByteArray > list = QImageWriter::supportedImageFormats(); for ( QList< QByteArray >::iterator it = list.begin(); it != list.end(); it++ ) { const char * str = (*it).constData(); m_write.push_back( QString(str) ); if ( strcasecmp( str, "JPEG" ) == 0 ) { m_write.push_back( QString("JPG") ); } if ( strcasecmp( str, "TIFF" ) == 0 ) { m_write.push_back( QString("TIF") ); } } } } } mm3d-1.3.15/src/implui/qttex.h000066400000000000000000000036571466047437300160760ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __QTTEX_H #define __QTTEX_H #include "texmgr.h" #include class QImage; class QtTextureFilter : public TextureFilter { public: QtTextureFilter(); virtual ~QtTextureFilter(); std::list< std::string > getReadTypes(); std::list< std::string > getWriteTypes(); Texture::ErrorE readFile( Texture * texture, const char * filename ); Texture::ErrorE writeFile( Texture * texture, const char * filename ); bool canRead( const char * filename ); bool canWrite( const char * filename ); Texture::ErrorE readMemory( const char * format, Texture * texture, const ImageData * d ); Texture::ErrorE writeMemory( const char * format, Texture * texture, ImageData ** d ); protected: void initializeSupported(); void imageToTexture( Texture * texture, QImage * image ); void textureToImage( Texture * texture, uint8_t * data ); void getFormatString( char * format, const char * filename ); bool m_initialized; QStringList m_read; QStringList m_write; }; #endif // __QTTEX_H mm3d-1.3.15/src/implui/smdprompt.cc000066400000000000000000000071321466047437300171040ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "smdprompt.h" #include "smdfilter.h" #include "model.h" #include "helpwin.h" #include #include #include SmdPrompt::SmdPrompt() : QDialog( NULL ) { setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } SmdPrompt::~SmdPrompt() { } void SmdPrompt::setOptions( SmdFilter::SmdOptions * opts, Model * model ) { m_saveMeshes->setChecked( opts->m_saveMeshes ); m_saveAnimation->setChecked( !opts->m_saveMeshes ); m_savePointsJoint->setChecked( opts->m_savePointsJoint ); m_singleVertexInfluence->setChecked( !opts->m_multipleVertexInfluences ); m_multipleVertexInfluences->setChecked( opts->m_multipleVertexInfluences ); m_singleVertexInfluence->setEnabled( opts->m_saveMeshes ); m_multipleVertexInfluences->setEnabled( opts->m_saveMeshes ); m_animList->setEnabled( !opts->m_saveMeshes ); m_animList->clear(); unsigned count = model->getAnimCount( Model::ANIMMODE_SKELETAL ); for ( unsigned t = 0; t < count; t++ ) { m_animList->insertItem( t, QString::fromUtf8( model->getAnimName( Model::ANIMMODE_SKELETAL, t ) ) ); } if ( count > 0 ) { m_animList->item(0)->setSelected( true ); } } void SmdPrompt::getOptions( SmdFilter::SmdOptions * opts ) { opts->m_saveMeshes = m_saveMeshes->isChecked(); opts->m_savePointsJoint= m_savePointsJoint->isChecked(); opts->m_multipleVertexInfluences = m_multipleVertexInfluences->isChecked(); opts->m_animations.clear(); if ( opts->m_saveMeshes ) { opts->m_animations.push_back( UINT_MAX ); } else { unsigned count = m_animList->count(); for ( unsigned t = 0; t < count; t++ ) { if ( m_animList->item( t )->isSelected() ) { opts->m_animations.push_back( t ); } } } } void SmdPrompt::helpNowEvent() { HelpWin * win = new HelpWin( "olh_smdprompt.html", true ); win->show(); } void SmdPrompt::saveMeshesChangedEvent() { m_singleVertexInfluence->setEnabled( m_saveMeshes->isChecked() ); m_multipleVertexInfluences->setEnabled( m_saveMeshes->isChecked() ); m_animList->setEnabled( m_saveAnimation->isChecked() ); } bool smdprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ) { bool rval = false; SmdPrompt p; SmdFilter::SmdOptions * opts = dynamic_cast< SmdFilter::SmdOptions * >( o ); if ( opts ) { opts->setOptionsFromModel( model ); p.setOptions( opts, model ); if ( p.exec() ) { rval = true; p.getOptions( opts ); } } else { rval = true; } return rval; } mm3d-1.3.15/src/implui/smdprompt.h000066400000000000000000000027341466047437300167510ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SMDPROMPT_H #define __SMDPROMPT_H #include "smdprompt.base.h" #include "modelfilter.h" #include "smdfilter.h" #include class Model; class SmdPrompt : public QDialog, public Ui::SmdPromptBase { Q_OBJECT public: SmdPrompt(); virtual ~SmdPrompt(); void setOptions( SmdFilter::SmdOptions * o, Model * model ); void getOptions( SmdFilter::SmdOptions * o ); public slots: void helpNowEvent(); void saveMeshesChangedEvent(); protected: }; bool smdprompt_show( Model * model, const char * const filename, ModelFilter::Options * o ); #endif // __SMDPROMPT_H mm3d-1.3.15/src/implui/spherifywin.cc000066400000000000000000000113511466047437300174240ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "spherifywin.h" #include "helpwin.h" #include "model.h" #include "glmath.h" #include "decalmgr.h" #include "log.h" #include #include #include #include #include SpherifyWin::SpherifyWin( Model * model, QWidget * parent ) : ValueWin( parent ), m_model( model ) { setAttribute( Qt::WA_DeleteOnClose ); setLabel( "Spherify" ); m_valueSlider->setMinimum( -100 ); m_valueEdit->setText( QString("0") ); double min[3] = { 0.0, 0.0, 0.0 }; double max[3] = { 0.0, 0.0, 0.0 }; bool haveCenter = false; SpherifyPosition sv; std::list posList; m_model->getSelectedPositions( posList ); std::list::iterator it; for ( it = posList.begin(); it != posList.end(); it++ ) { sv.pos = (*it); m_model->getPositionCoords( (*it), sv.coords ); if ( haveCenter ) { min[0] = sv.coords[0] < min[0] ? sv.coords[0] : min[0]; min[1] = sv.coords[1] < min[1] ? sv.coords[1] : min[1]; min[2] = sv.coords[2] < min[2] ? sv.coords[2] : min[2]; max[0] = sv.coords[0] > max[0] ? sv.coords[0] : max[0]; max[1] = sv.coords[1] > max[1] ? sv.coords[1] : max[1]; max[2] = sv.coords[2] > max[2] ? sv.coords[2] : max[2]; } else { min[0] = sv.coords[0]; min[1] = sv.coords[1]; min[2] = sv.coords[2]; max[0] = sv.coords[0]; max[1] = sv.coords[1]; max[2] = sv.coords[2]; haveCenter = true; } m_positions.push_back( sv ); } m_center[0] = (min[0] + max[0]) / 2.0; m_center[1] = (min[1] + max[1]) / 2.0; m_center[2] = (min[2] + max[2]) / 2.0; m_radius = 0.0; { SpherifyPositionList::iterator it; for ( it = m_positions.begin(); it != m_positions.end(); it++ ) { double dist = distance( m_center[0], m_center[1], m_center[2], (*it).coords[0], (*it).coords[1], (*it).coords[2] ); m_radius = dist > m_radius ? dist : m_radius; } } log_debug( "center is %f,%f,%f\n", m_center[0], m_center[1], m_center[2] ); log_debug( "radius is %f\n", m_radius ); } SpherifyWin::~SpherifyWin() { } void SpherifyWin::showHelp() { HelpWin * win = new HelpWin( "olh_spherifywin.html", true ); win->show(); } void SpherifyWin::valueSliderChanged( int v ) { log_debug( "changed\n" ); if ( v > 100 ) { v = 100; } if ( v < -100 ) { v = -100; } double percent = (double) v / 100.0; double diff[4] = { 0.0, 0.0, 0.0, 0.0 }; SpherifyPositionList::iterator it; for ( it = m_positions.begin(); it != m_positions.end(); it++ ) { diff[0] = (*it).coords[0] - m_center[0]; diff[1] = (*it).coords[1] - m_center[1]; diff[2] = (*it).coords[2] - m_center[2]; Vector vec( diff ); vec.normalize3(); diff[0] = vec.get( 0 ) * m_radius + m_center[0]; diff[1] = vec.get( 1 ) * m_radius + m_center[1]; diff[2] = vec.get( 2 ) * m_radius + m_center[2]; diff[0] = (diff[0] - (*it).coords[0]) * percent + (*it).coords[0]; diff[1] = (diff[1] - (*it).coords[1]) * percent + (*it).coords[1]; diff[2] = (diff[2] - (*it).coords[2]) * percent + (*it).coords[2]; m_model->movePosition( (*it).pos, diff[0], diff[1], diff[2] ); } DecalManager::getInstance()->modelUpdated( m_model ); ValueWin::valueSliderChanged( v ); } void SpherifyWin::valueEditChanged( const QString & str ) { ValueWin::valueEditChanged( str ); } void SpherifyWin::accept() { m_model->operationComplete( tr( "Spherify", "operation complete" ).toUtf8() ); DecalManager::getInstance()->modelUpdated( m_model ); ValueWin::accept(); } void SpherifyWin::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); ValueWin::reject(); } mm3d-1.3.15/src/implui/spherifywin.h000066400000000000000000000032061466047437300172660ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SPHERIFYWIN_H #define __SPHERIFYWIN_H #include "valuewin.h" #include "model.h" #include class Model; class SpherifyWin : public ValueWin { Q_OBJECT public: SpherifyWin( Model * model, QWidget * parent = NULL ); virtual ~SpherifyWin(); public slots: void valueEditChanged( const QString & ); void valueSliderChanged( int ); void accept(); void reject(); protected: virtual void showHelp(); typedef struct _SpherifyPosition_t { Model::Position pos; double coords[3]; } SpherifyPosition; typedef std::list SpherifyPositionList; Model * m_model; double m_center[3]; double m_radius; SpherifyPositionList m_positions; }; #endif // __SPHERIFYWIN_H mm3d-1.3.15/src/implui/statusbar.cc000066400000000000000000000161251466047437300170710ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "statusbar.h" #include "mm3dport.h" #include "model.h" #include "misc.h" #include #include #include #include #include #include using std::map; map StatusBar::s_modelMap; StatusBar::StatusBar( Model * model, QWidget * parent ) : QWidget( parent ), m_model( model ), m_queueDisplay( false ) { setupUi( this ); m_palette = this->palette(); this->setAutoFillBackground( true ); m_statusLabel->setAutoFillBackground( true ); m_labelFrame->setAutoFillBackground( true ); s_modelMap[ m_model ] = this; } StatusBar::~StatusBar() { s_modelMap.erase( m_model ); } StatusObject * StatusBar::getStatusBarFromModel( Model * model ) { if ( s_modelMap.find( model ) != s_modelMap.end() ) { return s_modelMap[ model ]; } else { return NULL; } } void StatusBar::setModel( Model * model ) { s_modelMap.erase( m_model ); m_model = model; s_modelMap[ m_model ] = this; } void StatusBar::setText( const char * str ) { QFontMetrics metrics( m_statusLabel->font() ); QString message = QString::fromUtf8( str ); QString visibleMessage = metrics.elidedText( message , Qt::ElideRight, m_statusLabel->width() ); m_statusLabel->setText( visibleMessage ); if ( visibleMessage != message ) { setToolTip( message ); } else { setToolTip( "" ); } } void StatusBar::addText( StatusTypeE type, unsigned ms, const char * str ) { if ( m_queueDisplay ) { // Clear non-errors first bool removing = true; size_t max_queue_size = 2; while ( removing && m_queue.size() > max_queue_size ) { removing = false; std::list< TextQueueItemT >::iterator it; for ( it = m_queue.begin(); it != m_queue.end(); it++ ) { if ( (*it).type != StatusError ) { m_queue.erase( it ); it = m_queue.end(); removing = true; } } } // If we still have more than max_queue_size in the queue, and the // new message is an error, start removing the oldest errors. if ( m_queue.size() > max_queue_size ) { if ( type != StatusError ) return; while ( m_queue.size() > max_queue_size ) m_queue.pop_front(); } TextQueueItemT tqi; tqi.str = QString::fromUtf8( str ); tqi.ms = ms; tqi.type = type; m_queue.push_back( tqi ); } else { setText( str ); QTimer::singleShot( ms, this, SLOT(timerExpired())); if ( type == StatusError ) { QPalette p = m_palette; p.setColor( QPalette::WindowText, QColor( 255, 255, 255 ) ); p.setColor( QPalette::Window, QColor( 255, 0, 0 ) ); m_labelFrame->setPalette( p ); } m_queueDisplay = true; } } void StatusBar::timerExpired() { m_labelFrame->setPalette( m_palette ); if ( !m_queue.empty() ) { TextQueueItemT tqi = m_queue.front(); m_queue.pop_front(); setText( tqi.str.toUtf8() ); m_queueDisplay = true; if ( tqi.type == StatusError ) { QPalette p = m_palette; p.setColor( QPalette::WindowText, QColor( 255, 255, 255 ) ); p.setColor( QPalette::Window, QColor( 255, 0, 0 ) ); m_labelFrame->setPalette( p ); } if ( tqi.ms > 0 ) { QTimer::singleShot( tqi.ms, this, SLOT(timerExpired())); } else { timerExpired(); } } else { m_queueDisplay = false; } } void StatusBar::setVertices( unsigned v, unsigned sv ) { QString statChar = tr( "V:", "Vertices status bar label" ); QString str; if ( sv ) { str = QString::asprintf( "%s%d/%d", (const char *) statChar.toUtf8(), sv, v ); } else { str = QString::asprintf( "%s%d", (const char *) statChar.toUtf8(), v ); } m_vertexLabel->setText( str ); } void StatusBar::setFaces( unsigned f, unsigned sf ) { QString statChar = tr( "F:", "Faces status bar label" ); QString str; if ( sf ) { str = QString::asprintf( "%s%d/%d", (const char *) statChar.toUtf8(), sf, f ); } else { str = QString::asprintf( "%s%d", (const char *) statChar.toUtf8(), f ); } m_faceLabel->setText( str ); } void StatusBar::setGroups( unsigned g, unsigned sg ) { QString statChar = tr( "G:", "Groups status bar label" ); QString str; if ( sg ) { str = QString::asprintf( "%s%d/%d", (const char *) statChar.toUtf8(), sg, g ); } else { str = QString::asprintf( "%s%d", (const char *) statChar.toUtf8(), g ); } m_groupLabel->setText( str ); } void StatusBar::setBoneJoints( unsigned b, unsigned sb ) { QString statChar = tr( "B:", "Bone Joints status bar label" ); QString str; if ( sb ) { str = QString::asprintf( "%s%d/%d", (const char *) statChar.toUtf8(), sb, b ); } else { str = QString::asprintf( "%s%d", (const char *) statChar.toUtf8(), b ); } m_boneLabel->setText( str ); } void StatusBar::setPoints( unsigned b, unsigned sb ) { QString statChar = tr( "P:", "Points status bar label" ); QString str; if ( sb ) { str = QString::asprintf( "%s%d/%d", (const char *) statChar.toUtf8(), sb, b ); } else { str = QString::asprintf( "%s%d", (const char *) statChar.toUtf8(), b ); } m_pointLabel->setText( str ); } void StatusBar::setTextures( unsigned t, unsigned st ) { QString statChar = tr( "M:", "Materials status bar label" ); QString str; if ( st ) { str = QString::asprintf( "%s%d/%d", (const char *) statChar.toUtf8(), st, t ); } else { str = QString::asprintf( "%s%d", (const char *) statChar.toUtf8(), t ); } m_textureLabel->setText( str ); } extern "C" void model_status( Model * model, StatusTypeE type, unsigned ms, const char * fmt, ... ) { static char temp[1024]; va_list ap; va_start( ap, fmt ); PORT_vsnprintf( temp, sizeof(temp), fmt, ap ); StatusObject * bar = StatusBar::getStatusBarFromModel( model ); if ( bar ) { bar->addText( type, ms, temp ); } else { if ( type == StatusError ) { model->pushError( temp ); } } } mm3d-1.3.15/src/implui/statusbar.h000066400000000000000000000045041466047437300167310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __STATUSBAR_H #define __STATUSBAR_H #include "statusbar.base.h" #include "modelstatus.h" #include #include #include #include class Model; class StatusBar : public QWidget, public Ui::StatusBarBase, public StatusObject { Q_OBJECT public: static StatusObject * getStatusBarFromModel( Model * model ); StatusBar( Model * model, QWidget * parent ); virtual ~StatusBar(); Model * getModel() { return m_model; }; void setModel( Model * m_model ); // Status text void setText( const char * str ); void addText( StatusTypeE type, unsigned ms, const char * str ); // Second (optional) argument is num selected void setVertices( unsigned v, unsigned sv = 0 ); void setFaces( unsigned f, unsigned sf = 0 ); void setGroups( unsigned g, unsigned sg = 0 ); void setBoneJoints( unsigned b, unsigned sb = 0 ); void setPoints( unsigned p, unsigned sp = 0 ); void setTextures( unsigned t, unsigned st = 0 ); public slots: void timerExpired(); protected: struct _TextQueueItem_t { StatusTypeE type; unsigned ms; QString str; }; typedef struct _TextQueueItem_t TextQueueItemT; Model * m_model; QPalette m_palette; std::list m_queue; bool m_queueDisplay; static std::map< Model *, StatusBar * > s_modelMap; }; #endif // __STATUSBAR_H mm3d-1.3.15/src/implui/texturecoord.cc000066400000000000000000000527511466047437300176150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "texturecoord.h" #include "textureframe.h" #include "texwidget.h" #include "model.h" #include "mapdirection.h" #include "log.h" #include "decalmgr.h" #include "helpwin.h" #include "keycfg.h" #include "3dmprefs.h" #include #include #include #include #include #include #include #include #include #include TextureCoord::TextureCoord( Model * model, QWidget * parent ) : QDialog( parent ), m_undoCount( 0 ), m_redoCount( 0 ), m_inUndo( false ), m_ignoreChange( false ), m_currentDirection( 0 ), m_currentMapScheme( 0 ) { setupUi( this ); // TODO handle undo of select/unselect? // Can't do this until after constructor is done because of observer interface //setModel( m_model ); m_textureFrame->setModel( model ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); QShortcut * undo = new QShortcut( QKeySequence( tr("CTRL+Z", "Undo shortcut")), this ); connect( undo, SIGNAL(activated()), this, SLOT(undoEvent()) ); QShortcut * redo = new QShortcut( QKeySequence( tr("CTRL+Y", "Redo shortcut")), this ); connect( redo, SIGNAL(activated()), this, SLOT(redoEvent()) ); QShortcut * select = new QShortcut( g_keyConfig.getKey( "tool_select_vertices" ), this ); connect( select, SIGNAL(activated()), this, SLOT(toolSelectEvent()) ); QShortcut * move = new QShortcut( g_keyConfig.getKey( "tool_move" ), this ); connect( move, SIGNAL(activated()), this, SLOT(toolMoveEvent()) ); QShortcut * rotate = new QShortcut( g_keyConfig.getKey( "tool_rotate" ), this ); connect( rotate, SIGNAL(activated()), this, SLOT(toolRotateEvent()) ); QShortcut * scale = new QShortcut( g_keyConfig.getKey( "tool_scale" ), this ); connect( scale, SIGNAL(activated()), this, SLOT(toolScaleEvent()) ); m_textureWidget = m_textureFrame->getTextureWidget(); m_textureWidget->setInteractive( true ); m_textureWidget->setDrawBorder( true ); g_prefs.setDefault( "ui_texcoord_scale_aspect", 0 ); g_prefs.setDefault( "ui_texcoord_scale_center", 1 ); bool aspect = ( g_prefs( "ui_texcoord_scale_aspect" ).intValue() != 0 ); bool center = ( g_prefs( "ui_texcoord_scale_center" ).intValue() != 0 ); m_scaleAspect->setChecked( aspect ); m_scaleCenter->setChecked( center ); m_textureWidget->setScaleKeepAspect( m_scaleAspect->isChecked() ); m_textureWidget->setScaleFromCenter( m_scaleCenter->isChecked() ); connect( m_textureWidget, SIGNAL(updateCoordinatesSignal()), this, SLOT(updateTextureCoordsEvent())); connect( m_textureWidget, SIGNAL(updateSelectionDoneSignal()), this, SLOT(updateSelectionDoneEvent())); connect( m_textureWidget, SIGNAL(updateCoordinatesDoneSignal()), this, SLOT(updateDoneEvent())); connect( m_textureWidget, SIGNAL(zoomLevelChanged(QString)), this, SLOT(zoomLevelChangedEvent(QString)) ); m_selectButton->setChecked( true ); m_textureWidget->setMouseOperation( TextureWidget::MouseSelect ); g_prefs.setDefault( "ui_texcoord_lines_color", 0xffffff ); g_prefs.setDefault( "ui_texcoord_selection_color", 0xff0000 ); uint32_t linesColor = g_prefs( "ui_texcoord_lines_color" ).intValue(); uint32_t selectionColor = g_prefs( "ui_texcoord_selection_color" ).intValue(); m_textureWidget->setLinesColor( linesColor ); m_textureWidget->setSelectionColor( selectionColor ); int linesIndex = 0; linesIndex |= (linesColor & 0x800000) ? 4 : 0 ; linesIndex |= (linesColor & 0x008000) ? 2 : 0 ; linesIndex |= (linesColor & 0x000080) ? 1 : 0 ; m_linesColor->setCurrentIndex( linesIndex ); int selectionIndex = 0; selectionIndex |= (selectionColor & 0x800000) ? 4 : 0 ; selectionIndex |= (selectionColor & 0x008000) ? 2 : 0 ; selectionIndex |= (selectionColor & 0x000080) ? 1 : 0 ; m_selectionColor->setCurrentIndex( selectionIndex ); } TextureCoord::~TextureCoord() { } void TextureCoord::undoEvent() { if ( m_undoCount > 0 ) { m_inUndo = true; m_model->undo(); m_undoCount--; m_inUndo = false; m_textureWidget->restoreSelectedUv(); m_textureWidget->update(); } } void TextureCoord::redoEvent() { if ( m_undoCount < m_redoCount ) { m_inUndo = true; m_model->redo(); m_undoCount++; m_inUndo = false; m_textureWidget->update(); } } void TextureCoord::operationComplete( const char * opname ) { m_undoCount++; m_redoCount = m_undoCount; m_ignoreChange = true; m_model->operationComplete( opname ); m_ignoreChange = false; } void TextureCoord::show() { setModel( m_model ); if ( !isVisible() ) { // If we are visible, setModel already did this initWindow(); } QDialog::show(); } void TextureCoord::initWindow() { m_currentMapScheme = MapSchemeGroup; m_currentDirection = 0; m_triangleButton->setChecked( false ); m_quadButton->setChecked( false ); m_groupButton->setChecked( true ); m_textureWidget->clearCoordinates(); bool foundTexture = false; list trilist; m_model->getSelectedTriangles( trilist ); list::iterator it; for ( it = trilist.begin(); !foundTexture && it != trilist.end(); it++ ) { int g = m_model->getTriangleGroup( *it ); int m = m_model->getGroupTextureId( g ); if ( m >= 0 ) { // FIXME cache current texture value and don't change it if we // don't have to (to prevent resetting zoom and center) m_textureFrame->textureChangedEvent( m + 1 ); foundTexture = true; } } if ( !foundTexture ) { log_error( "no group selected\n" ); m_textureFrame->textureChangedEvent( 0 ); } useGroupCoordinates(); DecalManager::getInstance()->modelUpdated( m_model ); if ( m_inUndo ) m_textureWidget->restoreSelectedUv(); else m_textureWidget->saveSelectedUv(); m_textureWidget->update(); } void TextureCoord::setModel( Model * model ) { m_undoCount = 0; m_redoCount = 0; if ( model != m_model ) { model->addObserver( this ); } m_model = model; m_textureFrame->setModel( model ); if ( isVisible() ) { initWindow(); } } void TextureCoord::closeEvent( QCloseEvent * e ) { e->ignore(); hide(); } void TextureCoord::helpNowEvent() { HelpWin * win = new HelpWin( "olh_texturecoordwin.html", true ); win->show(); } void TextureCoord::toolSelectEvent() { m_selectButton->setChecked( true ); m_textureWidget->setMouseOperation( TextureWidget::MouseSelect ); m_textureWidget->update(); } void TextureCoord::toolMoveEvent() { m_moveButton->setChecked( true ); m_textureWidget->setMouseOperation( TextureWidget::MouseMove ); m_textureWidget->update(); } void TextureCoord::toolRotateEvent() { m_rotateButton->setChecked( true ); m_textureWidget->setMouseOperation( TextureWidget::MouseRotate ); m_textureWidget->update(); } void TextureCoord::toolScaleEvent() { m_scaleButton->setChecked( true ); m_textureWidget->setMouseOperation( TextureWidget::MouseScale ); m_textureWidget->update(); } void TextureCoord::mapGroupEvent() { if ( m_currentMapScheme == MapSchemeGroup ) { return; } MapDirection dir(this); dir.setMapDirection( getDefaultDirection() ); if ( dir.exec() ) { mapGroup( dir.getMapDirection() ); } else { cancelMapChange(); return; } m_textureWidget->update(); } void TextureCoord::resetClickedEvent() { if ( m_currentMapScheme == MapSchemeGroup ) { MapDirection dir(this); if ( dir.exec() ) { mapGroup( dir.getMapDirection() ); m_textureWidget->update(); log_debug( "reset texture coordinates and map from direction %d\n", dir.getMapDirection() ); } } else { if ( QMessageBox::Ok == QMessageBox::warning( this, tr("Reset coordinates?", "window title"), tr("Are you sure you want to reset texture coordinates for this group?"), QMessageBox::Ok, QMessageBox::Cancel ) ) { log_debug( "reset texture coordinates\n" ); switch ( m_currentMapScheme ) { case MapSchemeTriangle: mapTriangle(); break; case MapSchemeQuad: mapQuad(); break; default: break; } } } } void TextureCoord::updateDoneEvent() { operationComplete( tr("Move texture coordinates").toUtf8() ); } void TextureCoord::updateSelectionDoneEvent() { m_textureWidget->saveSelectedUv(); operationComplete( tr("Select texture coordinates").toUtf8() ); } void TextureCoord::updateTextureCoordsEvent() { list trilist; m_model->getSelectedTriangles( trilist ); if ( trilist.size() > 0 ) { float s[3]; float t[3]; if ( m_currentMapScheme == MapSchemeGroup ) { list::iterator it; for( it = trilist.begin(); it != trilist.end(); it++ ) { if ( m_model->isTriangleSelected( *it ) ) { m_textureWidget->getCoordinates( m_textureTriangles[ *it ].m_triangleNum, s, t ); for ( int v = 0; v < 3; v++ ) { m_model->setTextureCoords( *it, v, s[v], t[v] ); } } } } else { list::iterator it; list::iterator texIt = m_triangles.begin(); for( it = trilist.begin(); it != trilist.end(); it++ ) { if ( m_model->isTriangleSelected( *it ) ) { if ( texIt == m_triangles.end() ) { texIt = m_triangles.begin(); } m_textureWidget->getCoordinates( *texIt, s, t ); for ( int v = 0; v < 3; v++ ) { m_model->setTextureCoords( *it, v, s[v], t[v] ); } texIt++; } } } DecalManager::getInstance()->modelUpdated( m_model ); } else { log_error( "no group selected\n" ); } } void TextureCoord::scaleSettingsChangedEvent() { m_textureWidget->setScaleKeepAspect( m_scaleAspect->isChecked() ); m_textureWidget->setScaleFromCenter( m_scaleCenter->isChecked() ); g_prefs( "ui_texcoord_scale_aspect" ) = m_scaleAspect->isChecked() ? 1 : 0; g_prefs( "ui_texcoord_scale_center" ) = m_scaleCenter->isChecked() ? 1 : 0; } void TextureCoord::rotateCcwEvent() { m_textureWidget->rotateCoordinatesCcw(); updateTextureCoordsEvent(); updateDoneEvent(); } void TextureCoord::rotateCwEvent() { m_textureWidget->rotateCoordinatesCw(); updateTextureCoordsEvent(); updateDoneEvent(); } void TextureCoord::vFlipEvent() { m_textureWidget->vFlipCoordinates(); updateTextureCoordsEvent(); updateDoneEvent(); } void TextureCoord::hFlipEvent() { m_textureWidget->hFlipCoordinates(); updateTextureCoordsEvent(); updateDoneEvent(); } void TextureCoord::selectionColorChangedEvent( int newColor ) { uint32_t rgb = 0; rgb |= (newColor & 1) ? 0x0000ff : 0; rgb |= (newColor & 2) ? 0x00ff00 : 0; rgb |= (newColor & 4) ? 0xff0000 : 0; m_textureWidget->setSelectionColor( rgb ); m_textureWidget->update(); g_prefs( "ui_texcoord_selection_color" ) = (int) rgb; } void TextureCoord::linesColorChangedEvent( int newColor ) { uint32_t rgb = 0; rgb |= (newColor & 1) ? 0x0000ff : 0; rgb |= (newColor & 2) ? 0x00ff00 : 0; rgb |= (newColor & 4) ? 0xff0000 : 0; m_textureWidget->setLinesColor( rgb ); m_textureWidget->update(); g_prefs( "ui_texcoord_lines_color" ) = (int) rgb; } void TextureCoord::zoomIn() { m_textureWidget->zoomIn(); } void TextureCoord::zoomOut() { m_textureWidget->zoomOut(); } void TextureCoord::zoomChangeEvent() { double zoom = m_zoomInput->text().toDouble(); if ( zoom < 0.00001 ) { zoom = 1; } m_textureWidget->setZoomLevel( zoom ); } void TextureCoord::zoomLevelChangedEvent( QString zoomStr ) { m_zoomInput->setText( zoomStr ); } void TextureCoord::mapTriangle() { if ( MapSchemeTriangle == m_currentMapScheme ) { return; } m_currentMapScheme = MapSchemeTriangle; m_textureWidget->clearCoordinates(); clearTriangles(); double min = 0.0; //m_textureWidget->getMinViewCoord(); double max = 1.0; //m_textureWidget->getMaxViewCoord(); int v1 = m_textureWidget->addVertex( min, max ); int v2 = m_textureWidget->addVertex( min, min ); int v3 = m_textureWidget->addVertex( max, min ); m_triangles.push_back( m_textureWidget->addTriangle( v1, v2, v3 ) ); updateTextureCoordsEvent(); updateDoneEvent(); m_textureWidget->update(); } void TextureCoord::mapQuad() { if ( MapSchemeQuad == m_currentMapScheme ) { return; } m_currentMapScheme = MapSchemeQuad; m_textureWidget->clearCoordinates(); clearTriangles(); double min = 0.0; //m_textureWidget->getMinViewCoord(); double max = 1.0; //m_textureWidget->getMaxViewCoord(); int v1 = m_textureWidget->addVertex( min, max ); int v2 = m_textureWidget->addVertex( max, max ); int v3 = m_textureWidget->addVertex( min, min ); int v4 = m_textureWidget->addVertex( max, min ); m_triangles.push_back( m_textureWidget->addTriangle( v4, v2, v1 ) ); m_triangles.push_back( m_textureWidget->addTriangle( v1, v3, v4 ) ); updateTextureCoordsEvent(); updateDoneEvent(); m_textureWidget->update(); } void TextureCoord::mapGroup( int direction ) { log_debug( "mapGroup( %d )\n", direction ); m_currentMapScheme = MapSchemeGroup; double range = 1.0; //m_textureWidget->getMaxViewCoord() - m_textureWidget->getMinViewCoord(); double xOff = 0.0; //m_textureWidget->getMinViewCoord(); double yOff = xOff; m_textureWidget->clearCoordinates(); clearTriangles(); m_textureTriangles.clear(); m_textureVertices.clear(); bool setBounds = true; double xMin = 0.0; double yMin = 0.0; double xMax = 0.0; double yMax = 0.0; double coord[2]; list trilist; m_model->getSelectedTriangles( trilist ); if ( trilist.size() > 0 ) { list::iterator it; for( it = trilist.begin(); it != trilist.end(); it++ ) { if ( m_model->isTriangleSelected( *it ) ) { for ( int t = 0; t < 3; t++ ) { int v = m_model->getTriangleVertex( *it, t ); m_model->getVertexCoords2d( v, static_cast (direction+1), coord ); if ( setBounds ) { xMin = xMax = coord[0]; yMin = yMax = coord[1]; setBounds = false; } else { if ( coord[0] < xMin ) { xMin = coord[0]; } else if ( coord[0] > xMax ) { xMax = coord[0]; } if ( coord[1] < yMin ) { yMin = coord[1]; } else if ( coord[1] > yMax ) { yMax = coord[1]; } } } } } log_debug( "Bounds = (%f,%f) - (%f,%f)\n", xMin, yMin, xMax, yMax ); for( it = trilist.begin(); it != trilist.end(); it++ ) { if ( m_model->isTriangleSelected( *it ) ) { int t; int vert[3]; for ( t = 0; t < 3; t++ ) { int v = m_model->getTriangleVertex( *it, t ); m_model->getVertexCoords2d( v, static_cast (direction+1), coord ); if ( m_textureVertices.find( v ) != m_textureVertices.end() ) { vert[t] = m_textureVertices[ v ]; } else { vert[t] = m_textureWidget->addVertex( (((coord[0] - xMin) / (xMax - xMin)) * range) + xOff, (((coord[1] - yMin) / (yMax - yMin)) * range) + yOff); m_textureVertices[ v ] = vert[t]; } } int tri = m_textureWidget->addTriangle( vert[0], vert[1], vert[2] ); TextureTriangleT texTri; texTri.m_triangleNum = tri; for ( t = 0; t < 3; t++ ) { texTri.m_vertexNum[t] = vert[t]; } m_textureTriangles[ *it ] = texTri; m_triangles.push_back( tri ); } } } else { log_error( "no triangles selected\n" ); } updateTextureCoordsEvent(); updateDoneEvent(); m_textureWidget->update(); } void TextureCoord::clearTriangles() { while ( m_triangles.size() ) { m_triangles.pop_front(); } } void TextureCoord::close() { hide(); } void TextureCoord::useGroupCoordinates() { m_textureTriangles.clear(); m_textureVertices.clear(); float m = 0.0; list trilist; m_model->getSelectedTriangles( trilist ); if ( trilist.size() > 0 ) { list::iterator it; for( it = trilist.begin(); it != trilist.end(); it++ ) { if ( m_model->isTriangleSelected( *it ) ) { float tCoord; float sCoord; int vert[3]; int t; for ( t = 0; t < 3; t++ ) { m_model->getTextureCoords( *it, t, sCoord, tCoord ); vert[t] = m_textureWidget->addVertex( sCoord, tCoord ); sCoord = fabs( sCoord ); tCoord = fabs( tCoord ); sCoord = ( sCoord > tCoord ) ? sCoord : tCoord; if ( sCoord > m ) { m = sCoord; } } int tri = m_textureWidget->addTriangle( vert[0], vert[1], vert[2] ); TextureTriangleT texTri; texTri.m_triangleNum = tri; for ( t = 0; t < 3; t++ ) { texTri.m_vertexNum[t] = vert[t]; } m_textureTriangles[ *it ] = texTri; m_triangles.push_back( tri ); } } log_debug( "max texture coordinate value is %f\n", m ); } else { log_error( "no group selected\n" ); } } int TextureCoord::getDefaultDirection() { list trilist; m_model->getSelectedTriangles( trilist ); if ( trilist.size() > 0 ) { list::iterator it; float total[3]; float normal[3]; int t; for ( t = 0; t < 3; t++ ) { total[t] = 0; } for( it = trilist.begin(); it != trilist.end(); it++ ) { if ( m_model->isTriangleSelected( *it ) ) { for ( t = 0; t < 3; t++ ) { m_model->getNormal( *it, t, normal ); for ( int v = 0; v < 3; v++ ) { total[v] += normal[v]; } } } } int index = 0; for ( t = 1; t < 3; t++ ) { if ( fabs(total[t]) > fabs(total[index]) ) { index = t; } } switch( index ) { case 0: if ( total[index] >= 0.0 ) { return 3; } else { return 2; } break; case 1: if ( total[index] >= 0.0 ) { return 4; } else { return 5; } break; case 2: if ( total[index] >= 0.0 ) { return 0; } else { return 1; } break; default: log_error( "bad normal index: %d\n", index ); return 0; } } else { log_error( "No group selected\n" ); } return 0; } void TextureCoord::cancelMapChange() { switch ( m_currentMapScheme ) { case MapSchemeTriangle: m_triangleButton->setChecked( true ); break; case MapSchemeQuad: m_quadButton->setChecked( true ); break; case MapSchemeGroup: m_groupButton->setChecked( true ); break; default: break; } } void TextureCoord::modelChanged( int changeBits ) { if ( !m_ignoreChange ) { if ( !m_inUndo ) { m_undoCount = 0; m_redoCount = 0; } if ( isVisible() ) { initWindow(); } } } mm3d-1.3.15/src/implui/texturecoord.h000066400000000000000000000064121466047437300174500ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TEXTURECOORD_H #define __TEXTURECOORD_H #include "texturecoord.base.h" #include "model.h" #include #include #include using std::list; using std::map; class TextureWidget; class TextureCoord : public QDialog, public Ui::TextureCoordBase, public Model::Observer { Q_OBJECT public: TextureCoord( Model * model, QWidget * parent = NULL ); virtual ~TextureCoord(); enum MapSchemeTypes { MapSchemeTriangle = 0, MapSchemeQuad = 1, MapSchemeGroup = 2 }; enum ToolTypes { ToolSelect = 0, ToolMove = 1, ToolScale = 2 }; // Model::Observer methods void modelChanged( int changeBits ); public slots: void show(); void closeEvent( QCloseEvent * e ); void helpNowEvent(); void toolSelectEvent(); void toolMoveEvent(); void toolRotateEvent(); void toolScaleEvent(); void setModel( Model * m ); void resetClickedEvent(); void zoomLevelChangedEvent(QString); void zoomChangeEvent(); void scaleSettingsChangedEvent(); void updateTextureCoordsEvent(); void updateSelectionDoneEvent(); void updateDoneEvent(); void rotateCcwEvent(); void rotateCwEvent(); void vFlipEvent(); void hFlipEvent(); void selectionColorChangedEvent(int); void linesColorChangedEvent(int); void zoomIn(); void zoomOut(); void undoEvent(); void redoEvent(); void mapTriangle(); void mapQuad(); void mapGroupEvent(); void mapGroup( int direction ); void close(); protected: struct _TextureTriangle_t { int m_triangleNum; int m_vertexNum[3]; }; typedef struct _TextureTriangle_t TextureTriangleT; void initWindow(); void operationComplete( const char * opname ); void clearTriangles(); void useGroupCoordinates(); int getDefaultDirection(); void cancelMapChange(); TextureWidget * m_textureWidget; Model * m_model; int m_undoCount; int m_redoCount; bool m_inUndo; bool m_ignoreChange; int m_currentDirection; int m_currentMapScheme; list m_triangles; map m_textureTriangles; map m_textureVertices; }; #endif // __TEXTURECOORD_H mm3d-1.3.15/src/implui/texwin.cc000066400000000000000000000347021466047437300164000ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "texwin.h" #include "textureframe.h" #include "texwidget.h" #include "model.h" #include "texture.h" #include "texmgr.h" #include "log.h" #include "decalmgr.h" #include "msg.h" #include "3dmprefs.h" #include "helpwin.h" #include "errorobj.h" #include #include #include #include #include #include #include #include #include #include using std::list; using std::string; TextureWindow::TextureWindow( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ), m_editing( false ), m_setting( false ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); m_textureFrame->setModel( model ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); int count = m_model->getTextureCount(); for ( int t = 0; t < count; t++ ) { //Texture * texture = m_model->getTextureData( t ); //if ( texture ) { m_textureComboBox->insertItem( t+1, QString::fromUtf8( m_model->getTextureName(t) ) ); } } int textureId = -1; list triangles; m_model->getSelectedTriangles( triangles ); list::iterator it; for ( it = triangles.begin(); it != triangles.end(); it++ ) { int g = m_model->getTriangleGroup( *it ); if ( g >= 0 ) { textureId = m_model->getGroupTextureId( g ); break; } } m_textureComboBox->setCurrentIndex( textureId + 1 ); textureChangedEvent( textureId + 1 ); m_lightingValue->setCurrentIndex(1); // Diffuse lightValueChanged( m_lightingValue->currentIndex() ); int previewIndex = 0; if ( g_prefs.exists( "ui_texwin_preview_index" ) ) { int val = g_prefs( "ui_texwin_preview_index" ).intValue(); if ( val >= 0 && val <= 1 ) { previewIndex = val; } } m_previewType->setCurrentIndex( previewIndex ); previewValueChanged( previewIndex ); } TextureWindow::~TextureWindow() { } void TextureWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_texturewin.html", true ); win->show(); } void TextureWindow::changeTextureFileEvent() { list formats = TextureManager::getInstance()->getAllReadTypes(); QString formatsStr = tr( "All Supported Formats (", "all texture formats" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += " "; } } formatsStr += ")"; QString dir = QString::fromUtf8( g_prefs( "ui_texture_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = "."; } QFileDialog d(NULL, "", dir, formatsStr + QString(";; All Files (*)" ) ); d.setWindowTitle( tr("Open texture image") ); d.selectNameFilter( formatsStr ); int execval = d.exec(); QStringList files = d.selectedFiles(); if ( QDialog::Accepted == execval && !files.empty() ) { std::string file = (const char *) files[0].toUtf8(); QString path = d.directory().absolutePath().toUtf8(); g_prefs( "ui_texture_dir" ) = (const char *) path.toUtf8(); Texture * tex = TextureManager::getInstance()->getTexture( file.c_str() ); if ( tex ) { int textureId = m_textureComboBox->currentIndex() - 1; m_model->setMaterialTexture( textureId, tex ); log_debug( "changed texture %d to %s\n", textureId, file.c_str() ); textureChangedEvent( textureId + 1 ); } else { QString err = tr(file.c_str()) + "\n"; Texture::ErrorE e = TextureManager::getInstance()->getLastError(); if ( e != Texture::ERROR_NONE ) { err += textureErrStr( e ); } else { err += tr("Could not open file"); } msg_error( (const char *) err.toUtf8() ); } } } void TextureWindow::noTextureFileEvent() { int textureId = m_textureComboBox->currentIndex() - 1; m_model->removeMaterialTexture( textureId ); log_debug( "removed texture from material %d\n", textureId ); textureChangedEvent( textureId + 1 ); } void TextureWindow::newMaterialClickedEvent() { bool ok = false; QString name = QInputDialog::getText( this, tr( "New Material", "window title" ), tr( "Enter new material name:" ), QLineEdit::Normal, QString(""), &ok ); if ( ok ) { int num = m_model->addColorMaterial( name.toUtf8() ); m_textureComboBox->insertItem( num+1, QString::fromUtf8( m_model->getTextureName(num)) ); m_textureComboBox->setCurrentIndex( num+1 ); textureChangedEvent( num+1 ); log_debug( "added %s as %d\n", (const char *) name.toUtf8(), num ); } } void TextureWindow::renameClickedEvent() { int textureId = m_textureComboBox->currentIndex() - 1; if ( textureId >= 0 ) { bool ok = false; QString name = QInputDialog::getText( this, tr( "Rename material", "window title" ), tr( "Enter new material name:" ), QLineEdit::Normal, QString::fromUtf8( m_model->getTextureName( textureId ) ), &ok ); if ( ok ) { m_model->setTextureName( textureId, name.toUtf8() ); m_textureComboBox->setItemText( textureId + 1, name ); } } } void TextureWindow::deleteClickedEvent() { int id = m_textureComboBox->currentIndex(); if ( id > 0 ) { m_model->deleteTexture( id - 1 ); m_textureComboBox->removeItem( id ); m_textureComboBox->setCurrentIndex( 0 ); textureChangedEvent( 0 ); } } void TextureWindow::clampSChangedEvent( int index ) { int id = m_textureComboBox->currentIndex() - 1; if ( id >= 0 ) { m_model->setTextureSClamp( id, (index == 1) ? true : false ); m_textureFrame->getTextureWidget()->setSClamp( (index == 1) ? true : false ); } } void TextureWindow::clampTChangedEvent( int index ) { int id = m_textureComboBox->currentIndex() - 1; if ( id >= 0 ) { m_model->setTextureTClamp( id, (index == 1) ? true : false ); m_textureFrame->getTextureWidget()->setTClamp( (index == 1) ? true : false ); } } void TextureWindow::textureChangedEvent( int id ) { m_textureFrame->textureChangedEvent( id ); id = id - 1; if ( id >= 0 ) { m_redSlider->setEnabled( true ); m_greenSlider->setEnabled( true ); m_blueSlider->setEnabled( true ); m_alphaSlider->setEnabled( true ); m_redEdit->setEnabled( true ); m_greenEdit->setEnabled( true ); m_blueEdit->setEnabled( true ); m_alphaEdit->setEnabled( true ); m_lightingValue->setEnabled( true ); m_deleteButton->setEnabled( true ); m_renameButton->setEnabled( true ); m_sClamp->setEnabled( true ); m_tClamp->setEnabled( true ); m_fileButton->setEnabled( true ); lightValueChanged( m_lightingValue->currentIndex() ); m_sClamp->setCurrentIndex( (m_model->getTextureSClamp( id )) ? 1 : 0 ); m_tClamp->setCurrentIndex( (m_model->getTextureTClamp( id )) ? 1 : 0 ); m_textureFrame->getTextureWidget()->setSClamp( m_model->getTextureSClamp( id ) ); m_textureFrame->getTextureWidget()->setTClamp( m_model->getTextureTClamp( id ) ); } else { m_redSlider->setEnabled( false ); m_greenSlider->setEnabled( false ); m_blueSlider->setEnabled( false ); m_alphaSlider->setEnabled( false ); m_redEdit->setEnabled( false ); m_greenEdit->setEnabled( false ); m_blueEdit->setEnabled( false ); m_alphaEdit->setEnabled( false ); m_lightingValue->setEnabled( false ); m_deleteButton->setEnabled( false ); m_renameButton->setEnabled( false ); m_sClamp->setEnabled( false ); m_tClamp->setEnabled( false ); m_fileButton->setEnabled( false ); m_noFileButton->setEnabled( false ); } updateChangeButton(); } void TextureWindow::updateEvent() { int id = (unsigned) m_textureComboBox->currentIndex() - 1; float val[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; val[0] = m_redSlider->value() / 100.0; val[1] = m_greenSlider->value() / 100.0; val[2] = m_blueSlider->value() / 100.0; val[3] = m_alphaSlider->value() / 100.0; switch ( m_lightingValue->currentIndex() ) { case 0: m_model->setTextureAmbient( id, val ); break; case 1: m_model->setTextureDiffuse( id, val ); break; case 2: m_model->setTextureSpecular( id, val ); break; case 3: m_model->setTextureEmissive( id, val ); break; case 4: m_model->setTextureShininess( id, val[0] * 100.0); break; default: break; } DecalManager::getInstance()->modelUpdated( m_model ); m_textureFrame->getTextureWidget()->update(); } void TextureWindow::accept() { m_model->operationComplete( tr("Texture changes").toUtf8() ); QDialog::accept(); DecalManager::getInstance()->modelUpdated( m_model ); } void TextureWindow::reject() { m_model->undoCurrent(); DecalManager::getInstance()->modelUpdated( m_model ); QDialog::reject(); } void TextureWindow::previewValueChanged( int index ) { m_textureFrame->set3d( (index == 1) ? true : false ); g_prefs( "ui_texwin_preview_index" ) = index; } void TextureWindow::lightValueChanged( int index ) { int id = (unsigned) m_textureComboBox->currentIndex() - 1; float val[4]; bool haveLighting = false; switch ( m_lightingValue->currentIndex() ) { case 0: // Ambient haveLighting = m_model->getTextureAmbient( id, val); break; case 1: // Diffuse haveLighting = m_model->getTextureDiffuse( id, val); break; case 2: // Specular haveLighting = m_model->getTextureSpecular( id, val); break; case 3: // Emissive haveLighting = m_model->getTextureEmissive( id, val); break; case 4: // Shininess haveLighting = m_model->getTextureShininess( id, val[0] ); break; default: break; } if ( m_lightingValue->currentIndex() == 4 ) { m_redLabel->setText( tr( "Shininess" ) ); m_redSlider->setMaximum( 100 ); m_redSlider->setMinimum( 0 ); m_greenLabel->hide(); m_greenSlider->hide(); m_greenEdit->hide(); m_blueLabel->hide(); m_blueSlider->hide(); m_blueEdit->hide(); m_alphaLabel->hide(); m_alphaSlider->hide(); m_alphaEdit->hide(); } else { m_redLabel->setText( tr( "Red" ) ); m_redSlider->setMaximum( 100 ); m_redSlider->setMinimum( -100 ); m_greenLabel->show(); m_greenSlider->show(); m_greenEdit->show(); m_blueLabel->show(); m_blueSlider->show(); m_blueEdit->show(); m_alphaLabel->show(); m_alphaSlider->show(); m_alphaEdit->show(); } if ( haveLighting ) { m_setting = true; m_redSlider->setValue( (int) (val[0] * 100) ); m_greenSlider->setValue( (int) (val[1] * 100) ); m_blueSlider->setValue( (int) (val[2] * 100) ); m_alphaSlider->setValue( (int) (val[3] * 100) ); m_setting = false; } else { log_error( "could not get lighting values for %d\n", id ); } } //------------------------------------------------------------------ // RGBA Functions //------------------------------------------------------------------ void TextureWindow::redSliderChanged( int v ) { if ( ! m_editing ) { QString str = QString::asprintf( "%1.02f", (float) v / 100.0 ); m_redEdit->setText( str ); } if ( !m_setting ) { updateEvent(); } } void TextureWindow::greenSliderChanged( int v ) { if ( ! m_editing ) { QString str = QString::asprintf( "%1.02f", (float) v / 100.0 ); m_greenEdit->setText( str ); } if ( !m_setting ) { updateEvent(); } } void TextureWindow::blueSliderChanged( int v ) { if ( ! m_editing ) { QString str = QString::asprintf( "%1.02f", (float) v / 100.0 ); m_blueEdit->setText( str ); } if ( !m_setting ) { updateEvent(); } } void TextureWindow::alphaSliderChanged( int v ) { if ( ! m_editing ) { QString str = QString::asprintf( "%1.02f", (float) v / 100 ); m_alphaEdit->setText( str ); } if ( !m_setting ) { updateEvent(); } } void TextureWindow::redEditChanged( const QString & str ) { m_editing = true; float v = str.toDouble(); m_redSlider->setValue( (int) (v * 100) ); m_editing = false; } void TextureWindow::greenEditChanged( const QString & str ) { m_editing = true; float v = str.toDouble(); m_greenSlider->setValue( (int) (v * 100) ); m_editing = false; } void TextureWindow::blueEditChanged( const QString & str ) { m_editing = true; float v = str.toDouble(); m_blueSlider->setValue( (int) (v * 100) ); m_editing = false; } void TextureWindow::alphaEditChanged( const QString & str ) { m_editing = true; float v = str.toDouble(); m_alphaSlider->setValue( (int) (v * 100) ); m_editing = false; } void TextureWindow::updateChangeButton() { int textureId = m_textureComboBox->currentIndex() - 1; if ( textureId >= 0 ) { if ( m_model->getMaterialType( textureId ) == Model::Material::MATTYPE_TEXTURE ) { m_fileButton->setText( tr( "Change texture...", "Change material's texture file" ) ); m_noFileButton->setEnabled( true ); } else { m_fileButton->setText( tr( "Set texture...", "Add texture file to material" ) ); m_noFileButton->setEnabled( false ); } } } mm3d-1.3.15/src/implui/texwin.h000066400000000000000000000041551466047437300162410ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TEXWIN_H #define __TEXWIN_H #include "texwin.base.h" #include class Model; class TextureWindow : public QDialog, public Ui::TextureWindowBase { Q_OBJECT public: TextureWindow( Model * model, QWidget * parent = NULL ); virtual ~TextureWindow(); public slots: void helpNowEvent(); void updateEvent(); void accept(); void reject(); void textureChangedEvent(int id ); void changeTextureFileEvent(); void noTextureFileEvent(); void newMaterialClickedEvent(); void renameClickedEvent(); void deleteClickedEvent(); void clampSChangedEvent(int index ); void clampTChangedEvent(int index ); void lightValueChanged( int ); void previewValueChanged( int ); void redSliderChanged( int ); void greenSliderChanged( int ); void blueSliderChanged( int ); void alphaSliderChanged( int ); void redEditChanged( const QString & ); void greenEditChanged( const QString & ); void blueEditChanged( const QString & ); void alphaEditChanged( const QString & ); protected: void updateChangeButton(); Model * m_model; bool m_editing; bool m_setting; }; #endif // __TEXWIN_H mm3d-1.3.15/src/implui/transformwin.cc000066400000000000000000000121141466047437300176040ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "transformwin.h" #include "textureframe.h" #include "texwidget.h" #include "model.h" #include "texture.h" #include "texmgr.h" #include "log.h" #include "decalmgr.h" #include "msg.h" #include "3dmprefs.h" #include "helpwin.h" #include #include #include #include #include #include #include #include #include #include using std::list; using std::string; TransformWindow::TransformWindow( Model * model, QWidget * parent ) : QDialog( parent ), m_model( model ) { setupUi( this ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } TransformWindow::~TransformWindow() { } void TransformWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_transformwin.html", true ); win->show(); } void TransformWindow::close() { QDialog::hide(); } void TransformWindow::setModel( Model * m ) { m_model = m; } void TransformWindow::translateEvent() { double x = m_transX->text().toDouble(); double y = m_transY->text().toDouble(); double z = m_transZ->text().toDouble(); Matrix m; m.setTranslation( x, y, z ); applyMatrix( m, tr("Matrix Translate") ); } void TransformWindow::rotateEulerEvent() { double vec[3]; vec[0] = m_rotX->text().toDouble(); vec[1] = m_rotY->text().toDouble(); vec[2] = m_rotZ->text().toDouble(); vec[0] *= PIOVER180; // convert to radians vec[1] *= PIOVER180; // convert to radians vec[2] *= PIOVER180; // convert to radians Matrix m; m.setRotation( vec ); applyMatrix( m, tr("Matrix Rotate") ); } void TransformWindow::rotateQuaternionEvent() { double vec[3]; vec[0] = m_axisX->text().toDouble(); vec[1] = m_axisY->text().toDouble(); vec[2] = m_axisZ->text().toDouble(); double angle = m_angle->text().toDouble(); angle = angle * PIOVER180; // convert to radians Matrix m; m.setRotationOnAxis( vec, angle ); applyMatrix( m, tr("Matrix Rotate On Axis") ); } void TransformWindow::scaleEvent() { double x = m_scaleX->text().toDouble(); double y = m_scaleY->text().toDouble(); double z = m_scaleZ->text().toDouble(); Matrix m; m.set( 0, 0, x ); m.set( 1, 1, y ); m.set( 2, 2, z ); applyMatrix( m, tr("Matrix Scale") ); } void TransformWindow::matrixEvent() { Matrix m; m.set( 0, 0, m_00->text().toDouble() ); m.set( 0, 1, m_01->text().toDouble() ); m.set( 0, 2, m_02->text().toDouble() ); m.set( 0, 3, m_03->text().toDouble() ); m.set( 1, 0, m_10->text().toDouble() ); m.set( 1, 1, m_11->text().toDouble() ); m.set( 1, 2, m_12->text().toDouble() ); m.set( 1, 3, m_13->text().toDouble() ); m.set( 2, 0, m_20->text().toDouble() ); m.set( 2, 1, m_21->text().toDouble() ); m.set( 2, 2, m_22->text().toDouble() ); m.set( 2, 3, m_23->text().toDouble() ); m.set( 3, 0, m_30->text().toDouble() ); m.set( 3, 1, m_31->text().toDouble() ); m.set( 3, 2, m_32->text().toDouble() ); m.set( 3, 3, m_33->text().toDouble() ); applyMatrix( m, tr("Apply Matrix") ); } bool TransformWindow::matrixIsUndoable( const Matrix & m ) { if ( fabs( m.getDeterminant() ) >= 0.0006 ) return true; else return false; } bool TransformWindow::warnNoUndo( bool undoable ) { if ( !undoable ) { if ( QMessageBox::warning( NULL, tr("Transform Cannot Be Undone", "window title"), tr("This transformation cannot be undone.") + QString("\n") + tr("Are you sure you wish to continue?" ), tr("Apply Transformation", "button" ), tr("Cancel Transformation", "button" ) ) == 0 ) return true; else return false; } return true; } void TransformWindow::applyMatrix( const Matrix & m, const QString & action ) { bool undoable = matrixIsUndoable( m ); if ( warnNoUndo( undoable ) ) { Model::OperationScopeE scope = (m_scope->currentIndex() == 0) ? Model::OS_Selected : Model::OS_Global; m_model->applyMatrix( m, scope, true, undoable ); m_model->operationComplete( action.toUtf8() ); DecalManager::getInstance()->modelUpdated( m_model ); } } mm3d-1.3.15/src/implui/transformwin.h000066400000000000000000000032641466047437300174540ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TRANSFORMWIN_H #define __TRANSFORMWIN_H #include "transformwin.base.h" #include "glmath.h" #include class Model; class TransformWindow : public QDialog, public Ui::TransformWindowBase { Q_OBJECT public: TransformWindow( Model * model, QWidget * parent = NULL ); virtual ~TransformWindow(); bool matrixIsUndoable( const Matrix & m ); bool warnNoUndo( bool undoable ); public slots: // Transform window events void translateEvent(); void rotateEulerEvent(); void rotateQuaternionEvent(); void scaleEvent(); void matrixEvent(); void setModel( Model * m ); void helpNowEvent(); void close(); protected: void applyMatrix( const Matrix & m, const QString & action ); Model * m_model; }; #endif // __TRANSFORMWIN_H mm3d-1.3.15/src/implui/transimp.cc000066400000000000000000000022121466047437300167060ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "transimp.h" #include #include "translate.h" std::string _qt_translate( const char * str ) { return std::string( (const char *) qApp->translate( "LowLevel", str ).toUtf8() ); } void transimp_install_translator() { transll_install_handler( _qt_translate ); } mm3d-1.3.15/src/implui/transimp.h000066400000000000000000000017051466047437300165560ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TRANSIMP_H #define __TRANSIMP_H void transimp_install_translator(); #endif // __TRANSIMP_H mm3d-1.3.15/src/implui/valuewin.cc000066400000000000000000000045011466047437300167060ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "valuewin.h" #include "helpwin.h" #include "log.h" #include #include #include #include #include #include ValueWin::ValueWin( QWidget * parent, bool modal, Qt::WindowFlags flags ) : QDialog( parent, flags ), m_editing( false ) { setupUi( this ); setModal( modal ); m_valueEdit->setText( QString( "0" ) ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); } ValueWin::~ValueWin() { } void ValueWin::helpNowEvent() { showHelp(); } void ValueWin::showHelp() { HelpWin * win = new HelpWin( "olh_valuewin.html", true ); win->show(); } void ValueWin::setLabel( const char * newLabel ) { if ( newLabel ) { setWindowTitle( QString( newLabel ) ); QString str = QString::asprintf( "%s", newLabel ); m_propertyLabel->setText( str ); } } float ValueWin::getValue() { return (float) m_valueSlider->value(); } void ValueWin::setValue( const float & v ) { m_valueSlider->setValue( (int) v ); } void ValueWin::valueSliderChanged( int v ) { if ( ! m_editing ) { QString str = QString::asprintf( "%d", v ); m_valueEdit->setText( str ); } } void ValueWin::valueEditChanged( const QString & str ) { m_editing = true; float v = str.toDouble(); m_valueSlider->setValue( (int) v ); m_editing = false; } mm3d-1.3.15/src/implui/valuewin.h000066400000000000000000000033571466047437300165600ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __VALUEWIN_H #define __VALUEWIN_H #include "valuewin.base.h" #include class ValueWin : public QDialog, public Ui::ValueWinBase { Q_OBJECT public: ValueWin( QWidget * parent = NULL, bool modal = true, Qt::WindowFlags flags = Qt::WindowFlags() ); virtual ~ValueWin(); void setLabel( const char * newLabel ); float getValue(); void setValue( const float & v ); public slots: void helpNowEvent(); virtual void valueEditChanged( const QString & ); virtual void valueSliderChanged( int ); protected: virtual void showHelp(); // We don't want to update the edit box if the user is typing in it // So we set this to true when editing. When we update the slider, // our slot will check this value. If false, it will update the edit box bool m_editing; }; #endif // __VALUEWIN_H mm3d-1.3.15/src/implui/viewpanel.cc000066400000000000000000000227141466047437300170540ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "viewpanel.h" #include "mview.h" #include "toolbox.h" #include "model.h" #include "log.h" #include "msg.h" #include "modelviewport.h" #include "3dmprefs.h" #include #include ViewPanel::ViewPanel( Toolbox * toolbox, QWidget * parent ) : QWidget( parent ), m_model( NULL ), m_viewCount( 4 ), m_tall( false ), m_toolbox( toolbox ) { if ( g_prefs.exists( "ui_viewport_count" ) ) { m_viewCount = g_prefs( "ui_viewport_count" ).intValue(); if ( m_viewCount < 1 || m_viewCount > 9 ) { m_viewCount = 4; } } if ( g_prefs.exists( "ui_viewport_tall" ) ) { int tall = g_prefs( "ui_viewport_tall" ).intValue(); m_tall = ( tall != 0 ); } makeViews(); for ( unsigned s = 0; s < VIEWPORT_STATE_MAX; s++ ) { m_viewState[s].rotation[0] = -1000.0; } show(); } ViewPanel::~ViewPanel() { log_debug( "deleting view panel\n" ); } void ViewPanel::freeTextures() { for ( unsigned t = 0; t < m_viewCount; t++ ) { m_modelView[t]->freeTextures(); } } void ViewPanel::setModel( Model * model ) { if ( m_model != NULL && model != NULL ) { model->setPerspectiveDrawMode(m_model->getPerspectiveDrawMode()); model->setCanvasDrawMode(m_model->getCanvasDrawMode()); } for ( unsigned t = 0; t < m_viewCount; t++ ) { m_modelView[t]->setModel( model ); } m_model = model; } void ViewPanel::modelUpdatedEvent() { for ( unsigned t = 0; t < m_viewCount; t++ ) { m_modelView[t]->updateView(); } } void ViewPanel::frameArea( double x1, double y1, double z1, double x2, double y2, double z2 ) { for ( unsigned t = 0; t < m_viewCount; t++ ) { m_modelView[t]->frameArea( x1, y1, z1, x2, y2, z2 ); } } void ViewPanel::wireframeEvent() { m_model->setPerspectiveDrawMode( ModelViewport::ViewWireframe ); modelUpdatedEvent(); } void ViewPanel::flatEvent() { m_model->setPerspectiveDrawMode( ModelViewport::ViewFlat ); modelUpdatedEvent(); } void ViewPanel::smoothEvent() { m_model->setPerspectiveDrawMode( ModelViewport::ViewSmooth ); modelUpdatedEvent(); } void ViewPanel::textureEvent() { m_model->setPerspectiveDrawMode( ModelViewport::ViewTexture ); modelUpdatedEvent(); } void ViewPanel::alphaEvent() { m_model->setPerspectiveDrawMode( ModelViewport::ViewAlpha ); modelUpdatedEvent(); } void ViewPanel::canvasWireframeEvent() { m_model->setCanvasDrawMode( ModelViewport::ViewWireframe ); modelUpdatedEvent(); } void ViewPanel::canvasFlatEvent() { m_model->setCanvasDrawMode( ModelViewport::ViewFlat ); modelUpdatedEvent(); } void ViewPanel::canvasSmoothEvent() { m_model->setCanvasDrawMode( ModelViewport::ViewSmooth ); modelUpdatedEvent(); } void ViewPanel::canvasTextureEvent() { m_model->setCanvasDrawMode( ModelViewport::ViewTexture ); modelUpdatedEvent(); } void ViewPanel::canvasAlphaEvent() { m_model->setCanvasDrawMode( ModelViewport::ViewAlpha ); modelUpdatedEvent(); } void ViewPanel::makeViews() { g_prefs( "ui_viewport_count" ) = (int) m_viewCount; g_prefs( "ui_viewport_tall" ) = (int) m_tall ? 1 : 0; int width = 3; int height = 3; m_gridLayout = new QGridLayout( this ); m_gridLayout->setSpacing(0); m_gridLayout->setMargin(0); switch ( m_viewCount ) { case 1: width = 1; height = 1; break; case 2: if ( m_tall ) { width = 1; height = 2; } else { width = 2; height = 1; } break; case 4: width = 2; height = 2; break; case 6: if ( m_tall ) { width = 2; height = 3; } else { width = 3; height = 2; } break; default: width = 3; height = 3; break; } for ( unsigned t = 0; t < m_viewCount; t++ ) { m_modelView[t] = new ModelView( m_toolbox, this ); connect( m_modelView[t]->getModelViewport(), SIGNAL(viewportSaveState(int, const ModelViewport::ViewStateT & )), this, SLOT(viewportSaveStateEvent(int, const ModelViewport::ViewStateT &)) ); connect( m_modelView[t]->getModelViewport(), SIGNAL(viewportRecallState(int)), this, SLOT(viewportRecallStateEvent(int)) ); m_gridLayout->addWidget( m_modelView[t], t / width, t % width ); } setModel( m_model ); setDefaultViewDirections(); if ( m_model ) { double x1, y1, z1, x2, y2, z2; if ( m_model->getBoundingRegion( &x1, &y1, &z1, &x2, &y2, &z2 ) ) { frameArea( x1, y1, z1, x2, y2, z2 ); } } } void ViewPanel::deleteViews() { for ( unsigned t = 0; t < m_viewCount; t++ ) { delete m_modelView[t]; } delete m_gridLayout; } void ViewPanel::setDefaultViewDirections() { switch ( m_viewCount ) { case 1: m_modelView[0]->setViewDirection( ModelViewport::ViewPerspective ); break; case 2: m_modelView[0]->setViewDirection( ModelViewport::ViewOrtho ); m_modelView[1]->setViewDirection( ModelViewport::ViewPerspective ); break; case 4: m_modelView[0]->setViewDirection( ModelViewport::ViewFront ); m_modelView[1]->setViewDirection( ModelViewport::ViewLeft ); m_modelView[2]->setViewDirection( ModelViewport::ViewTop ); m_modelView[3]->setViewDirection( ModelViewport::ViewPerspective ); break; case 6: if ( m_tall ) { m_modelView[0]->setViewDirection( ModelViewport::ViewFront ); m_modelView[1]->setViewDirection( ModelViewport::ViewBack ); m_modelView[2]->setViewDirection( ModelViewport::ViewLeft ); m_modelView[3]->setViewDirection( ModelViewport::ViewRight ); m_modelView[4]->setViewDirection( ModelViewport::ViewTop ); m_modelView[5]->setViewDirection( ModelViewport::ViewPerspective ); } else { m_modelView[0]->setViewDirection( ModelViewport::ViewFront ); m_modelView[1]->setViewDirection( ModelViewport::ViewBack ); m_modelView[2]->setViewDirection( ModelViewport::ViewTop ); m_modelView[3]->setViewDirection( ModelViewport::ViewLeft ); m_modelView[4]->setViewDirection( ModelViewport::ViewRight ); m_modelView[5]->setViewDirection( ModelViewport::ViewPerspective ); } break; default: m_modelView[0]->setViewDirection( ModelViewport::ViewFront ); m_modelView[1]->setViewDirection( ModelViewport::ViewBack ); m_modelView[4]->setViewDirection( ModelViewport::ViewRight ); m_modelView[2]->setViewDirection( ModelViewport::ViewTop ); m_modelView[5]->setViewDirection( ModelViewport::ViewBottom ); m_modelView[3]->setViewDirection( ModelViewport::ViewLeft ); m_modelView[6]->setViewDirection( ModelViewport::ViewOrtho ); m_modelView[7]->setViewDirection( ModelViewport::ViewOrtho ); m_modelView[8]->setViewDirection( ModelViewport::ViewPerspective ); break; } } void ViewPanel::view1() { hide(); deleteViews(); m_viewCount = 1; makeViews(); show(); } void ViewPanel::view1x2() { hide(); deleteViews(); m_viewCount = 2; m_tall = true; makeViews(); show(); } void ViewPanel::view2x1() { hide(); deleteViews(); m_viewCount = 2; m_tall = false; makeViews(); show(); } void ViewPanel::view2x2() { hide(); deleteViews(); m_viewCount = 4; makeViews(); show(); } void ViewPanel::view2x3() { hide(); deleteViews(); m_viewCount = 6; m_tall = true; makeViews(); show(); } void ViewPanel::view3x2() { hide(); deleteViews(); m_viewCount = 6; m_tall = false; makeViews(); show(); } void ViewPanel::view3x3() { hide(); deleteViews(); m_viewCount = 9; makeViews(); show(); } void ViewPanel::viewportSaveStateEvent( int slot, const ModelViewport::ViewStateT & viewState ) { if ( slot >= 0 && slot < VIEWPORT_STATE_MAX ) { m_viewState[slot] = viewState; } } void ViewPanel::viewportRecallStateEvent( int slot ) { if ( slot >= 0 && slot < VIEWPORT_STATE_MAX && m_viewState[slot].rotation[0] > -900.0 ) { for ( unsigned v = 0; v < m_viewCount; v++ ) { ModelViewport * m = m_modelView[v]->getModelViewport(); if ( m->hasFocus() ) { m_modelView[v]->setViewDirection( m_viewState[slot].direction ); m->setViewState( m_viewState[slot] ); } } } } mm3d-1.3.15/src/implui/viewpanel.h000066400000000000000000000051111466047437300167060ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __VIEWPANEL_H #define __VIEWPANEL_H #include #include #include "modelviewport.h" class QGridLayout; class ModelView; class Model; class Toolbox; class ViewPanel : public QWidget { Q_OBJECT public: enum { VIEWPORT_STATE_MAX = 9 }; ViewPanel( Toolbox * toolbox, QWidget * parent = NULL ); virtual ~ViewPanel(); void freeTextures(); void setModel( Model * model ); void frameArea( double x1, double y1, double z1, double x2, double y2, double z2 ); unsigned getModelViewCount() { return m_viewCount; }; ModelView * getModelView( unsigned index ) { return (index < m_viewCount) ? m_modelView[index] : NULL; }; public slots: void modelUpdatedEvent(); void wireframeEvent(); void flatEvent(); void smoothEvent(); void textureEvent(); void alphaEvent(); void canvasWireframeEvent(); void canvasFlatEvent(); void canvasSmoothEvent(); void canvasTextureEvent(); void canvasAlphaEvent(); void view1(); void view1x2(); void view2x1(); void view2x2(); void view2x3(); void view3x2(); void view3x3(); public slots: void viewportSaveStateEvent(int, const ModelViewport::ViewStateT &); void viewportRecallStateEvent(int); protected: void deleteViews(); void makeViews(); void setDefaultViewDirections(); Model * m_model; QGridLayout * m_gridLayout; ModelView * m_modelView[9]; ModelViewport::ViewStateT m_viewState[ VIEWPORT_STATE_MAX ]; unsigned m_viewCount; bool m_tall; Toolbox * m_toolbox; }; #endif // __VIEWPANEL_H mm3d-1.3.15/src/implui/viewportsettings.cc000066400000000000000000000066411466047437300205230ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "viewportsettings.h" #include "helpwin.h" #include "3dmprefs.h" #include #include #include ViewportSettings::ViewportSettings( QWidget * parent ) : QDialog( parent ) { setAttribute( Qt::WA_DeleteOnClose ); setupUi( this ); setModal( true ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); switch ( g_prefs( "ui_colorscheme_type" ).intValue() ) { default: case 0: m_colorsAuto->setChecked( true ); break; case 1: m_colorsLight->setChecked( true ); break; case 2: m_colorsDark->setChecked( true ); break; } QString temp; m_gridUnit->setText( QString( g_prefs( "ui_grid_inc" ).stringValue().c_str() ) ); m_3dGridUnit->setText( QString( g_prefs( "ui_3dgrid_inc" ).stringValue().c_str() ) ); m_3dGridLines->setText( QString( g_prefs( "ui_3dgrid_count" ).stringValue().c_str() ) ); switch ( g_prefs( "ui_grid_mode" ).intValue() ) { default: case 0: m_binaryGrid->setChecked( true ); break; case 1: m_decimalGrid->setChecked( true ); break; case 2: m_fixedGrid->setChecked( true ); break; } m_3dXY->setChecked( g_prefs( "ui_3dgrid_xy" ).intValue() != 0 ); m_3dXZ->setChecked( g_prefs( "ui_3dgrid_xz" ).intValue() != 0 ); m_3dYZ->setChecked( g_prefs( "ui_3dgrid_yz" ).intValue() != 0 ); } ViewportSettings::~ViewportSettings() { } void ViewportSettings::accept() { int color_scheme = 0; if ( m_colorsLight->isChecked() ) color_scheme = 1; else if ( m_colorsDark->isChecked() ) color_scheme = 2; g_prefs( "ui_colorscheme_type" ) = color_scheme; g_prefs( "ui_grid_inc" ) = (const char *) m_gridUnit->text().toLatin1(); g_prefs( "ui_3dgrid_inc" ) = (const char *) m_3dGridUnit->text().toLatin1(); g_prefs( "ui_3dgrid_count" ) = (const char *) m_3dGridLines->text().toLatin1(); int grid_mode = 0; if ( m_decimalGrid->isChecked() ) grid_mode = 1; else if (m_fixedGrid->isChecked() ) grid_mode = 2; g_prefs( "ui_grid_mode" ) = grid_mode; g_prefs( "ui_3dgrid_xy" ) = m_3dXY->isChecked() ? 1 : 0; g_prefs( "ui_3dgrid_xz" ) = m_3dXZ->isChecked() ? 1 : 0; g_prefs( "ui_3dgrid_yz" ) = m_3dYZ->isChecked() ? 1 : 0; QDialog::accept(); } void ViewportSettings::helpNowEvent() { HelpWin * win = new HelpWin( "olh_viewportsettings.html", true ); win->show(); } mm3d-1.3.15/src/implui/viewportsettings.h000066400000000000000000000024041466047437300203560ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __VIEWPORTSETTINGS_H #define __VIEWPORTSETTINGS_H #include "viewportsettings.base.h" #include class ViewportSettings : public QDialog, public Ui::ViewportSettingsBase { Q_OBJECT public: ViewportSettings( QWidget * parent = NULL ); virtual ~ViewportSettings(); public slots: void helpNowEvent(); void accept(); private: }; #endif // __VIEWPORTSETTINGS_H mm3d-1.3.15/src/implui/viewwin.cc000066400000000000000000002403371466047437300165550ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "pluginmgr.h" #include "viewwin.h" #include "viewpanel.h" #include "model.h" #include "tool.h" #include "toolbox.h" #include "cmdmgr.h" #include "viewportsettings.h" #include "groupclean.h" #include "groupwin.h" #include "texwin.h" #include "texturecoord.h" #include "painttexturewin.h" #include "log.h" #include "decalmgr.h" #include "msg.h" #include "statusbar.h" #include "modelstatus.h" #include "filtermgr.h" #include "misc.h" #include "helpwin.h" #include "licensewin.h" #include "aboutwin.h" #include "animsetwin.h" #include "animexportwin.h" #include "animwin.h" #include "animwidget.h" #include "metawin.h" #include "3dmprefs.h" #include "stdcmds.h" #include "pluginwin.h" #include "backgroundwin.h" #include "mergewin.h" #include "misc.h" #include "version.h" #include "texmgr.h" #include "sysconf.h" #include "contextpanel.h" #include "boolpanel.h" #include "projectionwin.h" #include "transformwin.h" #include "keycfg.h" #include "qtmain.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "errorobj.h" #include "luascript.h" #include "luaif.h" #include "pixmap/mm3dlogo-32x32.xpm" #include #include #include using std::list; using std::string; const char DOCK_FILENAME[] = "dock.dat"; const int DOCK_VERSION = 1; //using namespace QIconSet; typedef QAction * QActionPtr; typedef ::Tool * ToolPtr; typedef std::list< ViewWindow *> ViewWindowList; static ViewWindowList _winList; static bool _shuttingDown = false; CommandWidget::CommandWidget( QObject * parent, Model * model, bool * canEdit, Command * cmd, int index ) : QObject( parent ), m_model( model ), m_canEdit( canEdit ), m_cmd( cmd ), m_index( index ) { } CommandWidget::~CommandWidget() { } void CommandWidget::setModel( Model * m ) { m_model = m; } void CommandWidget::activateCommand( bool ) { if ( !(*m_canEdit) ) { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", tr("You are in animation mode, but there are no animations").toUtf8().data() ); return; } if ( m_cmd->activated( m_index, m_model ) ) { m_model->operationComplete( qApp->translate( "Command", (m_cmd)->getName( m_index ) ).toUtf8() ); DecalManager * mgr = DecalManager::getInstance(); mgr->modelUpdated( m_model ); } } bool ViewWindow::closeAllWindows() { bool noPrompt = true; bool doSave = false; std::list::iterator it; for ( it = _winList.begin(); noPrompt == true && it != _winList.end(); it++ ) { noPrompt = (*it)->getSaved(); } #ifndef CODE_DEBUG if ( noPrompt == false ) { char response = msg_warning_prompt( (const char *) tr("Some models are unsaved. Save before exiting?").toUtf8() ); if ( response == 'C' ) { return false; } if ( response == 'Y' ) { doSave = true; } } #endif // CODE_DEBUG bool abortQuit = false; for ( it = _winList.begin(); !abortQuit && it != _winList.end(); it++ ) { ViewWindow * win = (*it); if ( doSave && win->getSaved() == false ) { win->raise(); win->setAbortQuit( false ); win->saveModelEvent(); abortQuit = win->getAbortQuit(); } } if ( !abortQuit ) { while ( !_winList.empty() ) { ViewWindow * win = _winList.front(); win->hide(); win->deleteLater(); _winList.pop_front(); } } if ( abortQuit ) { return false; } else { return true; } } // open model in an existing window that doesn't have a model open or open a new window. bool ViewWindow::openModelInEmptyWindow( const char * filename ) { QWindow *window = NULL; std::list::iterator it; for ( it = _winList.begin(); it != _winList.end(); it++ ) { if ( !(*it)->emptyWindow() ) { continue; } if ( !(*it)->openModelInWindow( filename ) ) { return false; } // Unminimize and move window to foreground window = (*it)->windowHandle(); window->show(); window->raise(); window->requestActivate(); break; } if ( !window ) { return ViewWindow::openModel( filename ); } return false; } class MainWidget : public QWidget { public: MainWidget( QWidget * parent = NULL ); virtual ~MainWidget() {}; void addWidgetToLayout( QWidget * w ); protected: QVBoxLayout * m_layout; }; MainWidget::MainWidget( QWidget * parent ) : QWidget( parent ) { m_layout = new QVBoxLayout( this ); m_layout->setMargin(2); m_layout->setSpacing(2); } void MainWidget::addWidgetToLayout( QWidget * w ) { m_layout->addWidget( w ); } ViewWindow::ViewWindow( Model * model, QWidget * parent ) : QMainWindow( parent ), m_model( model ), m_animWin( NULL ), m_toolbox( NULL ), m_toolList( NULL ), m_toolButtons( NULL ), m_last( NULL ), m_currentTool( NULL ), m_canEdit( true ) { m_model->setUndoEnabled( false ); setAttribute( Qt::WA_DeleteOnClose ); _winList.push_back( this ); setWindowIcon( QPixmap((const char **) mm3dlogo_32x32_xpm) ); setWindowIconText( QString("Maverick Model 3D") ); QShortcut * help = new QShortcut( QKeySequence( tr("F1", "Help Shortcut")), this ); connect( help, SIGNAL(activated()), this, SLOT(helpNowEvent()) ); updateCaption(); m_toolbox = new Toolbox(); MainWidget * mainWidget = new MainWidget( this ); m_viewPanel = new ViewPanel( m_toolbox, mainWidget ); mainWidget->addWidgetToLayout( m_viewPanel ); m_statusBar = new StatusBar( m_model, mainWidget ); mainWidget->addWidgetToLayout( m_statusBar ); m_statusBar->setText( tr( "Press F1 for help using any window" ).toUtf8() ); m_animWin = new AnimWindow( m_model, false, this ); m_animWin->setObjectName( "mainwin_animwin" ); m_animWidget = m_animWin->getAnimWidget(); addDockWidget( Qt::BottomDockWidgetArea, m_animWin ); connect( m_animWin, SIGNAL(animWindowClosed()), this, SLOT(animationModeOff()) ); connect( m_animWidget, SIGNAL(animWindowClosed()), this, SLOT(animationModeOff()) ); connect( m_animWidget, SIGNAL(animInvalid()), this, SLOT(editDisableEvent()) ); connect( m_animWidget, SIGNAL(animValid()), this, SLOT(editEnableEvent()) ); m_contextPanel = new ContextPanel( this, m_viewPanel, this ); m_contextPanel->setObjectName( "mainwin_properties" ); m_contextPanel->setWindowTitle( tr( "Properties" ) ); connect( this, SIGNAL(modelChanged(Model*)), m_contextPanel, SLOT(setModel(Model*))); addDockWidget( Qt::RightDockWidgetArea, m_contextPanel ); m_boolPanel = new BoolPanel( m_model, this, m_viewPanel ); m_boolPanel->setObjectName( "mainwin_boolwin" ); connect( this, SIGNAL(modelChanged(Model*)), m_boolPanel, SLOT(setModel(Model*))); m_projectionWin = new ProjectionWin( m_model, this, m_viewPanel ); connect( this, SIGNAL(modelChanged(Model*)), m_projectionWin, SLOT(setModel(Model*))); m_textureCoordWin = new TextureCoord( m_model ); connect( this, SIGNAL(modelChanged(Model*)), m_textureCoordWin, SLOT(setModel(Model*))); m_transformWin = new TransformWindow( m_model, this ); connect( this, SIGNAL(modelChanged(Model*)), m_transformWin, SLOT(setModel(Model*))); addDockWidget( Qt::RightDockWidgetArea, m_boolPanel ); setModel( m_model ); tabifyDockWidget( m_contextPanel, m_boolPanel ); setCentralWidget( mainWidget ); m_mruMenu = new QMenu( this ); m_scriptMruMenu = new QMenu( this ); connect( m_mruMenu, SIGNAL(aboutToShow()), this, SLOT(fillMruMenu()) ); connect( m_mruMenu, SIGNAL(triggered(QAction*)), this, SLOT(openMru(QAction*)) ); connect( m_scriptMruMenu, SIGNAL(aboutToShow()), this, SLOT(fillScriptMruMenu()) ); connect( m_scriptMruMenu, SIGNAL(triggered(QAction*)), this, SLOT(openScriptMru(QAction*)) ); m_fileMenu = new QMenu( this ); m_fileMenu->addAction( tr("New", "File|New"), this, SLOT(newModelEvent()), g_keyConfig.getKey( "viewwin_file_new" ) ); m_fileMenu->addAction( tr("Open...", "File|Open"), this, SLOT(openModelEvent()), g_keyConfig.getKey( "viewwin_file_open" ) ); m_fileMenu->addAction( tr("Save", "File|Save"), this, SLOT(saveModelEvent()), g_keyConfig.getKey( "viewwin_file_save" ) ); m_fileMenu->addAction( tr("Save As...", "File|Save As"), this, SLOT(saveModelAsEvent()), g_keyConfig.getKey( "viewwin_file_save_as" ) ); m_fileMenu->addAction( tr("Export...", "File|Export"), this, SLOT(exportModelEvent()), g_keyConfig.getKey( "viewwin_file_export" ) ); m_fileMenu->addAction( tr("Export Selected...", "File|Export Selected"), this, SLOT(exportSelectedEvent()), g_keyConfig.getKey( "viewwin_file_export_selected" ) ); #ifdef HAVE_LUALIB m_fileMenu->addAction( tr("Run Script...", "File|Run Script"), this, SLOT(scriptEvent()), g_keyConfig.getKey( "viewwin_file_run_script" ) ); m_scriptMruMenu->setTitle( tr("Recent Scripts", "File|Recent Script") ); m_fileMenu->addMenu( m_scriptMruMenu ); #endif // HAVE_LUALIB m_fileMenu->addSeparator(); m_mruMenu->setTitle( tr("Recent Models", "File|Recent Models") ); m_fileMenu->addMenu( m_mruMenu ); m_fileMenu->addSeparator(); m_fileMenu->addAction( tr("Plugins...", "File|Plugins"), this, SLOT(pluginWindowEvent()), g_keyConfig.getKey( "viewwin_file_plugins" ) ); m_fileMenu->addSeparator(); m_fileMenu->addAction( tr("Close", "File|Close"), this, SLOT(close()), g_keyConfig.getKey( "viewwin_file_close" ) ); m_fileMenu->addAction( tr("Quit", "File|Quit"), this, SLOT(quitEvent()), g_keyConfig.getKey( "viewwin_file_quit" ) ); m_renderMenu = new QMenu( this ); QActionGroup * group; // Bones group group = new QActionGroup( this ); m_hideJointsItem = m_renderMenu->addAction( tr("Hide Joints", "View|Hide Joints"), this, SLOT(boneJointHide()), g_keyConfig.getKey( "viewwin_view_render_joints_hide" ) ); m_drawJointLinesItem = m_renderMenu->addAction( tr("Draw Joint Lines", "View|Draw Joint Lines"), this, SLOT(boneJointLines()), g_keyConfig.getKey( "viewwin_view_render_joints_lines" ) ); m_drawJointBonesItem = m_renderMenu->addAction( tr("Draw Joint Bones", "View|Draw Joint Bones"), this, SLOT(boneJointBones()), g_keyConfig.getKey( "viewwin_view_render_joints_bones" ) ); m_hideJointsItem->setCheckable( true ); m_drawJointLinesItem->setCheckable( true ); m_drawJointBonesItem->setCheckable( true ); group->addAction( m_hideJointsItem ); group->addAction( m_drawJointLinesItem ); group->addAction( m_drawJointBonesItem ); g_prefs.setDefault( "ui_draw_joints", (int) Model::JOINTMODE_BONES ); if ( g_prefs( "ui_draw_joints" ).intValue() == (int) Model::JOINTMODE_BONES ) m_drawJointBonesItem->setChecked( true ); else m_drawJointBonesItem->setChecked( true ); m_renderMenu->addSeparator(); // Projection group group = new QActionGroup( this ); m_renderProjections = m_renderMenu->addAction( tr("Draw Texture Projections", "View|Draw Texture Projections"), this, SLOT(renderProjections()), g_keyConfig.getKey( "viewwin_view_render_projections_show" ) ); m_noRenderProjections = m_renderMenu->addAction( tr("Hide Texture Projections", "View|Hide Texture Projections"), this, SLOT(noRenderProjections()), g_keyConfig.getKey( "viewwin_view_render_projections_hide" ) ); m_renderProjections->setCheckable( true ); m_noRenderProjections->setCheckable( true ); group->addAction( m_renderProjections ); group->addAction( m_noRenderProjections ); m_renderProjections->setChecked( true ); m_renderMenu->addSeparator(); // Bad texture group group = new QActionGroup( this ); m_renderBadItem = m_renderMenu->addAction( tr("Use Red Error Texture", "View|Use Red Error Texture"), this, SLOT(renderBadEvent()), g_keyConfig.getKey( "viewwin_view_render_badtex_red" ) ); m_noRenderBadItem = m_renderMenu->addAction( tr("Use Blank Error Texture", "View|Use Blank Error Texture"), this, SLOT(noRenderBadEvent()), g_keyConfig.getKey( "viewwin_view_render_badtex_blank" ) ); m_renderBadItem->setCheckable( true ); m_noRenderBadItem->setCheckable( true ); group->addAction( m_renderBadItem ); group->addAction( m_noRenderBadItem ); g_prefs.setDefault( "ui_render_bad_textures", 1 ); if ( g_prefs( "ui_render_bad_textures" ).intValue() != 0 ) m_renderBadItem->setChecked( true ); else m_noRenderBadItem->setChecked( true ); m_renderMenu->addSeparator(); // 3D Lines group group = new QActionGroup( this ); m_renderSelectionItem = m_renderMenu->addAction( tr("Render 3D Lines", "View|Render 3D Lines"), this, SLOT(renderSelectionEvent()), g_keyConfig.getKey( "viewwin_view_render_3d_lines_show" ) ); m_noRenderSelectionItem = m_renderMenu->addAction( tr("Hide 3D Lines", "View|Hide 3D Lines"), this, SLOT(noRenderSelectionEvent()), g_keyConfig.getKey( "viewwin_view_render_3d_lines_hide" ) ); m_renderSelectionItem->setCheckable( true ); m_noRenderSelectionItem->setCheckable( true ); group->addAction( m_renderSelectionItem ); group->addAction( m_noRenderSelectionItem ); g_prefs.setDefault( "ui_render_3d_selections", 0 ); if ( g_prefs( "ui_render_3d_selections" ).intValue() != 0 ) m_renderSelectionItem->setChecked( true ); else m_noRenderSelectionItem->setChecked( true ); m_renderMenu->addSeparator(); // Backfacing poly group group = new QActionGroup( this ); m_renderBackface = m_renderMenu->addAction( tr("Draw Back-facing Triangles", "View|Draw Back-facing Triangles"), this, SLOT(renderBackface()), g_keyConfig.getKey( "viewwin_view_render_backface_show" ) ); m_noRenderBackface = m_renderMenu->addAction( tr("Hide Back-facing Triangles", "View|Hide Back-facing Triangles"), this, SLOT(noRenderBackface()), g_keyConfig.getKey( "viewwin_view_render_backface_hide" ) ); m_renderBackface->setCheckable( true ); m_noRenderBackface->setCheckable( true ); group->addAction( m_renderBackface ); group->addAction( m_noRenderBackface ); g_prefs.setDefault( "ui_render_backface_cull", 0 ); if ( g_prefs( "ui_render_backface_cull" ).intValue() == 0 ) m_renderBackface->setChecked( true ); else m_noRenderBackface->setChecked( true ); m_viewMenu = new QMenu( this ); m_viewMenu->addAction( tr( "Frame All", "View|Frame"), this, SLOT(frameAllEvent()), g_keyConfig.getKey( "viewwin_view_frame_all" ) ); m_viewMenu->addAction( tr( "Frame Selected", "View|Frame"), this, SLOT(frameSelectedEvent()), g_keyConfig.getKey( "viewwin_view_frame_selected" ) ); m_viewMenu->addSeparator(); m_showContext = m_viewMenu->addAction( tr("Show Properties", "View|Show Properties"), this, SLOT(showContextEvent()), g_keyConfig.getKey( "viewwin_view_show_properties" ) ); connect( m_contextPanel, SIGNAL(panelHidden()), this, SLOT(contextPanelHidden()) ); m_renderMenu->setTitle( tr( "Render Options", "View|Render Options") ); m_viewMenu->addMenu( m_renderMenu ); m_viewMenu->addSeparator(); // 3D View group group = new QActionGroup(this); m_3dWire = group->addAction( m_viewMenu->addAction( tr( "3D Wireframe", "View|3D"), m_viewPanel, SLOT(wireframeEvent()), g_keyConfig.getKey( "viewwin_view_3d_wireframe" ) ) ); m_3dFlat = group->addAction( m_viewMenu->addAction( tr( "3D Flat", "View|3D"), m_viewPanel, SLOT(flatEvent()), g_keyConfig.getKey( "viewwin_view_3d_flat" ) ) ); m_3dSmooth = group->addAction( m_viewMenu->addAction( tr( "3D Smooth", "View|3D"), m_viewPanel, SLOT(smoothEvent()), g_keyConfig.getKey( "viewwin_view_3d_smooth" ) ) ); m_3dTexture = group->addAction( m_viewMenu->addAction( tr( "3D Texture", "View|3D"), m_viewPanel, SLOT(textureEvent()), g_keyConfig.getKey( "viewwin_view_3d_textured" ) ) ); m_3dAlpha = group->addAction( m_viewMenu->addAction( tr( "3D Alpha Blend", "View|3D"), m_viewPanel, SLOT(alphaEvent()), g_keyConfig.getKey( "viewwin_view_3d_alpha" ) ) ); m_viewMenu->addSeparator(); // Canvas Group group = new QActionGroup(this); m_canvasWire = group->addAction( m_viewMenu->addAction( tr( "Canvas Wireframe", "View|Canvas"), m_viewPanel, SLOT(canvasWireframeEvent()), g_keyConfig.getKey( "viewwin_view_ortho_wireframe" ) ) ); m_canvasFlat = group->addAction( m_viewMenu->addAction( tr( "Canvas Flat", "View|Canvas"), m_viewPanel, SLOT(canvasFlatEvent()), g_keyConfig.getKey( "viewwin_view_ortho_flat" ) ) ); m_canvasSmooth = group->addAction( m_viewMenu->addAction( tr( "Canvas Smooth", "View|Canvas"), m_viewPanel, SLOT(canvasSmoothEvent()), g_keyConfig.getKey( "viewwin_view_ortho_smooth" ) ) ); m_canvasTexture = group->addAction( m_viewMenu->addAction( tr( "Canvas Texture", "View|Canvas"), m_viewPanel, SLOT(canvasTextureEvent()), g_keyConfig.getKey( "viewwin_view_ortho_textured" ) ) ); m_canvasAlpha = group->addAction( m_viewMenu->addAction( tr( "Canvas Alpha Blend", "View|Canvas"), m_viewPanel, SLOT(canvasAlphaEvent()), g_keyConfig.getKey( "viewwin_view_ortho_alpha" ) ) ); m_viewMenu->addSeparator(); // Num viewports group group = new QActionGroup(this); m_view1 = group->addAction( m_viewMenu->addAction( tr( "1 View", "View|Viewports"), m_viewPanel, SLOT(view1()), g_keyConfig.getKey( "viewwin_view_1" ) ) ); m_view1x2 = group->addAction( m_viewMenu->addAction( tr( "1x2 View", "View|Viewports"), m_viewPanel, SLOT(view1x2()), g_keyConfig.getKey( "viewwin_view_1x2" ) ) ); m_view2x1 = group->addAction( m_viewMenu->addAction( tr( "2x1 View", "View|Viewports"), m_viewPanel, SLOT(view2x1()), g_keyConfig.getKey( "viewwin_view_2x1" ) ) ); m_view2x2 = group->addAction( m_viewMenu->addAction( tr( "2x2 View", "View|Viewports"), m_viewPanel, SLOT(view2x2()), g_keyConfig.getKey( "viewwin_view_2x2" ) ) ); m_view2x3 = group->addAction( m_viewMenu->addAction( tr( "2x3 View", "View|Viewports"), m_viewPanel, SLOT(view2x3()), g_keyConfig.getKey( "viewwin_view_2x3" ) ) ); m_view3x2 = group->addAction( m_viewMenu->addAction( tr( "3x2 View", "View|Viewports"), m_viewPanel, SLOT(view3x2()), g_keyConfig.getKey( "viewwin_view_3x2" ) ) ); m_view3x3 = group->addAction( m_viewMenu->addAction( tr( "3x3 View", "View|Viewports"), m_viewPanel, SLOT(view3x3()), g_keyConfig.getKey( "viewwin_view_3x3" ) ) ); m_3dWire->setCheckable( true ); m_3dFlat->setCheckable( true ); m_3dSmooth->setCheckable( true ); m_3dTexture->setCheckable( true ); m_3dAlpha->setCheckable( true ); m_3dTexture->setChecked( true ); m_canvasWire->setCheckable( true ); m_canvasFlat->setCheckable( true ); m_canvasSmooth->setCheckable( true ); m_canvasTexture->setCheckable( true ); m_canvasAlpha->setCheckable( true ); m_canvasWire->setChecked( true ); m_view1->setCheckable( true ); m_view1x2->setCheckable( true ); m_view2x1->setCheckable( true ); m_view2x2->setCheckable( true ); m_view2x3->setCheckable( true ); m_view3x2->setCheckable( true ); m_view3x3->setCheckable( true ); int count = 4; bool tall = false; if ( g_prefs.exists( "ui_viewport_count" ) ) count = g_prefs( "ui_viewport_count" ).intValue(); if ( g_prefs.exists( "ui_viewport_tall" ) ) tall = (g_prefs( "ui_viewport_tall" ).intValue() != 0); switch ( count ) { case 1: m_view1->setChecked( true ); break; case 2: if ( tall ) m_view1x2->setChecked( true ); else m_view2x1->setChecked( true ); break; default: case 4: m_view2x2->setChecked( true ); break; case 6: if ( tall ) m_view2x3->setChecked( true ); else m_view3x2->setChecked( true ); break; case 9: m_view3x3->setChecked( true ); break; } m_viewMenu->addSeparator(); m_viewMenu->addAction( tr( "Viewport Settings...", "View|Viewport Settings" ), this, SLOT(viewportSettingsEvent()), g_keyConfig.getKey( "viewwin_view_viewport_settings" ) ); m_snapMenu = new QMenu( this ); connect( m_snapMenu, SIGNAL(triggered(QAction*)), this, SLOT(snapToSelectedEvent(QAction*)) ); m_snapToGrid = m_snapMenu->addAction( tr("Grid", "Tools|Snap to Grid") ); m_snapToGrid->setShortcut( g_keyConfig.getKey( "tool_snap_to_grid" ) ); m_snapToGrid->setCheckable( true ); m_snapToVertex = m_snapMenu->addAction( tr("Vertex", "Tools|Snap to Vertex") ); m_snapToVertex->setShortcut( g_keyConfig.getKey( "tool_snap_to_vertex" ) ); m_snapToVertex->setCheckable( true ); if ( g_prefs.exists( "ui_snap_grid" ) && g_prefs( "ui_snap_grid" ).intValue() != 0 ) { m_snapToGrid->setChecked( true ); } if ( g_prefs.exists( "ui_snap_vertex" ) && g_prefs( "ui_snap_vertex" ).intValue() != 0 ) { m_snapToVertex->setChecked( true ); } m_toolMenu = new QMenu( this ); m_snapMenu->setTitle( tr("Snap To") ); m_toolMenu->addMenu( m_snapMenu ); m_toolMenu->addSeparator(); m_toolBar = new QToolBar( this ); m_toolBar->setObjectName( "mainwin_toolbar" ); addToolBar( m_toolBar ); m_toolBar->setWindowTitle( tr( "Tools" ) ); //m_toolBar->setHorizontallyStretchable( true ); //m_toolBar->setVerticallyStretchable( true ); initializeToolbox(); m_modelMenu = new QMenu( this ); m_modelMenu->addAction( tr("Undo"), this, SLOT(undoRequest()), QKeySequence( tr("Ctrl+Z", "Undo shortcut" ) ) ); m_modelMenu->addAction( tr("Redo"), this, SLOT(redoRequest()), QKeySequence( tr("Ctrl+Y", "Redo shortcut" ) ) ); m_modelMenu->addSeparator(); m_modelMenu->addAction( tr("Edit Model Meta Data...", "Model|Edit Model Meta Data"), this, SLOT(metaWindowEvent()), g_keyConfig.getKey( "viewwin_model_edit_meta_data" ) ); m_modelMenu->addAction( tr("Transform Model...", "Model|Transform Model"), this, SLOT(transformWindowEvent()), g_keyConfig.getKey( "viewwin_model_transform" ) ); m_modelMenu->addAction( tr("Boolean Operation...", "Model|Boolean Operation"), this, SLOT(boolWindowEvent()), g_keyConfig.getKey( "viewwin_model_boolean_operation" ) ); m_modelMenu->addSeparator(); m_modelMenu->addAction( tr("Set Background Image...", "Model|Set Background Image"), this, SLOT(backgroundWindowEvent()), g_keyConfig.getKey( "viewwin_model_set_background_image" ) ); m_modelMenu->addAction( tr("Merge...", "Model|Merge"), this, SLOT(mergeModelsEvent()), g_keyConfig.getKey( "viewwin_model_merge" ) ); m_modelMenu->addAction( tr("Import Animations...", "Model|Import Animations"), this, SLOT(mergeAnimationsEvent()), g_keyConfig.getKey( "viewwin_model_import_animations" ) ); m_geometryMenu = new QMenu( this ); m_materialsMenu = new QMenu( this ); m_materialsMenu->addAction( tr("Edit Groups...", "Materials|Edit Groups"), this, SLOT(groupWindowEvent()), g_keyConfig.getKey( "viewwin_groups_edit_groups" ) ); m_materialsMenu->addAction( tr("Edit Materials...", "Materials|Edit Materials"), this, SLOT(textureWindowEvent()), g_keyConfig.getKey( "viewwin_groups_edit_materials" ) ); m_materialsMenu->addAction( tr("Clean Up...", "Materials|Clean Up"), this, SLOT(groupCleanWindowEvent()), g_keyConfig.getKey( "viewwin_groups_cleanup" ) ); m_materialsMenu->addAction( tr("Reload Textures", "Materials|Reload Textures"), this, SLOT(reloadTexturesEvent()), g_keyConfig.getKey( "viewwin_groups_reload_textures" ) ); m_materialsMenu->addSeparator(); m_materialsMenu->addAction( tr("Edit Projection...", "Materials|Edit Projection"), this, SLOT(projectionWindowEvent()), g_keyConfig.getKey( "viewwin_groups_edit_projection" ) ); m_materialsMenu->addAction( tr("Edit Texture Coordinates...", "Materials|Edit Texture Coordinates"), this, SLOT(textureCoordEvent()), g_keyConfig.getKey( "viewwin_groups_edit_texture_coordinates" ) ); m_materialsMenu->addAction( tr("Paint Texture...", "Materials|Paint Texture"), this, SLOT(paintTextureEvent()), g_keyConfig.getKey( "viewwin_groups_paint_texture" ) ); m_jointsMenu = new QMenu( this ); m_jointsMenu->addAction( tr( "Edit Joints...", "Joints|Edit Joints"), this, SLOT(jointWinEvent()), g_keyConfig.getKey( "viewwin_joints_edit_joints" ) ); m_jointsMenu->addAction( tr( "Assign Selected to Joint", "Joints|Assign Selected to Joint"), this, SLOT(jointAssignSelectedToJoint()), g_keyConfig.getKey( "viewwin_joints_assign_selected" ) ); m_jointsMenu->addAction( tr( "Auto-Assign Selected...", "Joints|Auto-Assign Selected"), this, SLOT(jointAutoAssignSelected()), g_keyConfig.getKey( "viewwin_joints_auto_assign_selected" ) ); m_jointsMenu->addAction( tr( "Remove All Influences from Selected", "Joints|Remove All Influences from Selected"), this, SLOT(jointRemoveInfluencesFromSelected()), g_keyConfig.getKey( "viewwin_joints_remove_influences" ) ); m_jointsMenu->addAction( tr( "Remove Selected Joint from Influencing", "Joints|Remove Selected Joint from Influencing"), this, SLOT(jointRemoveInfluenceJoint()), g_keyConfig.getKey( "viewwin_joints_remove_joint" ) ); m_jointsMenu->addSeparator(); m_jointsMenu->addAction( tr( "Convert Multiple Influences to Single", "Joints|Convert Multiple Influences to Single"), this, SLOT(jointMakeSingleInfluence()), g_keyConfig.getKey( "viewwin_joints_make_single_influence" ) ); m_jointsMenu->addSeparator(); m_jointsMenu->addAction( tr( "Select Joint Influences", "Joints|Select Joint Influences"), this, SLOT(jointSelectInfluenceJoints()), g_keyConfig.getKey( "viewwin_joints_select_joint_influences" ) ); m_jointsMenu->addAction( tr( "Select Influenced Vertices", "Joints|Select Influenced Vertices"), this, SLOT(jointSelectInfluencedVertices()), g_keyConfig.getKey( "viewwin_joints_select_influenced_vertices" ) ); m_jointsMenu->addAction( tr( "Select Influenced Points", "Joints|Select Influenced Points"), this, SLOT(jointSelectInfluencedPoints()), g_keyConfig.getKey( "viewwin_joints_select_influenced_points" ) ); m_jointsMenu->addAction( tr( "Select Unassigned Vertices", "Joints|Select Unassigned Vertices"), this, SLOT(jointSelectUnassignedVertices()), g_keyConfig.getKey( "viewwin_joints_select_unassigned_vertices" ) ); m_jointsMenu->addAction( tr( "Select Unassigned Points", "Joints|Select Unassigned Points"), this, SLOT(jointSelectUnassignedPoints()), g_keyConfig.getKey( "viewwin_joints_select_unassigned_points" ) ); initializeCommands(); //m_scriptMenu = new QPopupMenu( this ); m_animMenu = new QMenu( this ); m_startAnimItem = m_animMenu->addAction( tr("Start Animation Mode...", "Animation|Start Animation Mode"), this, SLOT(startAnimationMode()), g_keyConfig.getKey( "viewwin_anim_start_mode" ) ); m_stopAnimItem = m_animMenu->addAction( tr("Stop Animation Mode", "Animation|Stop Animation Mode"), this, SLOT(stopAnimationMode()), g_keyConfig.getKey( "viewwin_anim_stop_mode" ) ); m_animMenu->addSeparator(); m_animSetsItem = m_animMenu->addAction( tr("Animation Sets...", "Animation|Animation Sets"), this, SLOT(animSetWindowEvent()), g_keyConfig.getKey( "viewwin_anim_animation_sets" ) ); m_animExportItem = m_animMenu->addAction( tr("Save Animation Images...", "Animation|Save Animation Images"), this, SLOT(animExportWindowEvent()), g_keyConfig.getKey( "viewwin_anim_save_images" ) ); m_animMenu->addSeparator(); m_animCopyFrame = m_animMenu->addAction( tr("Copy Animation Frame", "Animation|Copy Animation Frame"), this, SLOT(animCopyFrameEvent()), g_keyConfig.getKey( "viewwin_anim_frame_copy" ) ); m_animPasteFrame = m_animMenu->addAction( tr("Paste Animation Frame", "Animation|Paste Animation Frame"), this, SLOT(animPasteFrameEvent()), g_keyConfig.getKey( "viewwin_anim_frame_paste" ) ); m_animClearFrame = m_animMenu->addAction( tr("Clear Animation Frame", "Animation|Clear Animation Frame"), this, SLOT(animClearFrameEvent()), g_keyConfig.getKey( "viewwin_anim_frame_clear" ) ); m_animMenu->addSeparator(); m_animCopySelected = m_animMenu->addAction( tr("Copy Selected Keyframes", "Animation|Copy Animation Frame"), this, SLOT(animCopySelectedEvent()), g_keyConfig.getKey( "viewwin_anim_selected_copy" ) ); m_animPasteSelected = m_animMenu->addAction( tr("Paste Selected Keyframes", "Animation|Paste Animation Frame"), this, SLOT(animPasteSelectedEvent()), g_keyConfig.getKey( "viewwin_anim_selected_paste" ) ); m_animMenu->addSeparator(); m_animSetRotItem = m_animMenu->addAction( tr("Set Rotation Keyframe", "Animation|Set Rotation Keyframe"), this, SLOT(animSetRotEvent()), g_keyConfig.getKey( "viewwin_anim_set_rotation" ) ); m_animSetTransItem = m_animMenu->addAction( tr("Set Translation Keyframe", "Animation|Set Translation Keyframe"), this, SLOT(animSetTransEvent()), g_keyConfig.getKey( "viewwin_anim_set_translation" ) ); m_animClearRotItem = m_animMenu->addAction( tr("Clear Rotation Keyframe", "Animation|Clear Rotation Keyframe"), this, SLOT(animClearRotEvent()), g_keyConfig.getKey( "viewwin_anim_clear_rotation" ) ); m_animClearTransItem = m_animMenu->addAction( tr("Clear Translation Keyframe", "Animation|Clear Translation Keyframe"), this, SLOT(animClearTransEvent()), g_keyConfig.getKey( "viewwin_anim_clear_translation" ) ); m_stopAnimItem->setEnabled( false ); m_animSetRotItem->setEnabled( false ); m_animSetTransItem->setEnabled( false ); m_animClearRotItem->setEnabled( false ); m_animClearTransItem->setEnabled( false ); m_animCopyFrame->setEnabled( false ); m_animPasteFrame->setEnabled( false ); m_animClearFrame->setEnabled( false ); m_animCopySelected->setEnabled( false ); m_animPasteSelected->setEnabled( false ); m_helpMenu = new QMenu( this ); m_helpMenu->addAction( tr("Contents...", "Help|Contents"), this, SLOT(helpWindowEvent()), g_keyConfig.getKey( "viewwin_help_contents" ) ); m_helpMenu->addAction( tr("License...", "Help|License"), this, SLOT(licenseWindowEvent()), g_keyConfig.getKey( "viewwin_help_license" ) ); m_helpMenu->addAction( tr("About...", "Help|About"), this, SLOT(aboutWindowEvent()), g_keyConfig.getKey( "viewwin_help_about" ) ); m_menuBar = menuBar(); m_fileMenu->setTitle( tr("&File", "menu bar") ); m_menuBar->addMenu( m_fileMenu ); m_viewMenu->setTitle( tr("&View", "menu bar") ); m_menuBar->addMenu( m_viewMenu ); m_toolMenu->setTitle( tr("&Tools", "menu bar") ); m_menuBar->addMenu( m_toolMenu ); m_modelMenu->setTitle( tr("&Model", "menu bar") ); m_menuBar->addMenu( m_modelMenu ); m_geometryMenu->setTitle( tr("&Geometry", "menu bar") ); m_menuBar->addMenu( m_geometryMenu ); m_materialsMenu->setTitle( tr("Mate&rials", "menu bar") ); m_menuBar->addMenu( m_materialsMenu ); m_jointsMenu->setTitle( tr("&Influences", "menu bar") ); m_menuBar->addMenu( m_jointsMenu ); m_animMenu->setTitle( tr("&Animation", "menu bar") ); m_menuBar->addMenu( m_animMenu ); m_helpMenu->setTitle( tr("&Help", "menu bar") ); m_menuBar->addMenu( m_helpMenu ); resize( g_prefs.exists( "ui_viewwin_width") ? g_prefs( "ui_viewwin_width" ) : 900, g_prefs.exists( "ui_viewwin_height") ? g_prefs( "ui_viewwin_height" ) : 700 ); // If the window size including the title bar etc is the area of the screen // without taskbar etc then set the window to maximized. QRect screenSpace = QApplication::desktop()->availableGeometry( this ); bool windowMaximized = false; if ( g_prefs.exists( "ui_viewwin_lastframewidth") && g_prefs.exists( "ui_viewwin_lastframeheight") ) { int frameWidth = g_prefs( "ui_viewwin_lastframewidth"); int frameHeight = g_prefs( "ui_viewwin_lastframeheight"); //log_debug( "Window frame size %dx%d, available %dx%d\n", frameWidth, frameHeight, screenSpace.width(), screenSpace.height() ); windowMaximized = ( frameWidth >= screenSpace.width() && frameHeight >= screenSpace.height() ); } setMinimumSize( 520, 520 ); frameAllEvent(); m_viewPanel->modelUpdatedEvent(); if ( windowMaximized ) { showMaximized(); } else { show(); } QTimer * m_savedTimer = new QTimer(); connect( m_savedTimer, SIGNAL(timeout()), this, SLOT(savedTimeoutCheck()) ); m_savedTimer->start( 1000 ); QString windowPos; loadDockPositions(); stopAnimationMode(); m_model->setUndoEnabled( true ); m_model->clearUndo(); // FIXME hack if toolbar was hidden by loadDockPositions(), restore it. // https://github.com/zturtleman/mm3d/issues/28 if ( m_toolBar->isHidden() ) { m_toolBar->show(); } // This is a hack to prevent a minimized bool panel from blocking the left // side of the menu bar. if ( !m_boolPanel->isVisible() ) { m_boolPanel->show(); m_boolPanel->hide(); } } ViewWindow::~ViewWindow() { log_debug( "deleting view window\n" ); _winList.remove( this ); log_debug( "deleting view window %p, model %p\n", this, m_model ); DecalManager::getInstance()->unregisterModel( m_model ); m_viewPanel->freeTextures(); delete m_model; m_viewPanel->setModel( NULL ); model_show_alloc_stats(); // QToolBar actually deletes buttons, we just need to delete array delete[] m_toolButtons; if ( !_shuttingDown ) { if ( _winList.empty() ) { ui_exit(); } } delete m_toolbox; } void ViewWindow::setModel( Model * model ) { m_viewPanel->setModel( model ); m_statusBar->setModel( model ); m_transformWin->setModel( model ); m_model = model; if ( m_model ) { Model::DrawJointModeE jointMode = static_cast( g_prefs( "ui_draw_joints" ).intValue() ); if ( jointMode != Model::JOINTMODE_LINES ) { jointMode = Model::JOINTMODE_BONES; } m_model->setDrawJoints( jointMode ); g_prefs( "ui_draw_joints" ) = (int) jointMode; } updateCaption(); //m_contextPanel->setModel( m_model ); if ( m_animWin->isVisible() ) { m_animWidget->initialize( m_model, true ); // Treat it like an undo } m_viewPanel->modelUpdatedEvent(); emit modelChanged( m_model ); while ( m_model->hasErrors() ) { std::string str = m_model->popError(); model_status( m_model, StatusError, STATUSTIME_LONG, "%s", str.c_str() ); } if ( m_currentTool ) { m_currentTool->setModel( m_model ); } for ( CommandMenuItemList::iterator it = m_primitiveCommands.begin(); it != m_primitiveCommands.end(); ++it ) { (*it)->widget->setModel( m_model ); } } bool ViewWindow::emptyWindow() { if ( m_model ) { // if no model is loaded and default scene is unmodified return ( strcmp( m_model->getFilename(), "" ) == 0 && m_model->getSaved() == true && !m_model->canRedo() ); } return true; } bool ViewWindow::getSaved() { if ( m_model ) { return m_model->getSaved(); } return true; } void ViewWindow::resizeEvent ( QResizeEvent * e ) { int w = e->size().width(); int h = e->size().height(); /* m_menuBar->move( 0, 0 ); m_menuBar->resize( w, m_menuBar->height() ); m_viewPanel->move( 0, m_menuBar->height() ); m_viewPanel->resize( w, h - m_menuBar->height() - m_statusBar->height() ); m_statusBar->move( 0, h - m_statusBar->height() ); m_statusBar->resize( w, m_statusBar->height() ); */ g_prefs( "ui_viewwin_width" ) = w; g_prefs( "ui_viewwin_height" ) = h; QMainWindow::resizeEvent(e); g_prefs( "ui_viewwin_lastframewidth" ) = frameGeometry().width(); g_prefs( "ui_viewwin_lastframeheight" ) = frameGeometry().height(); //log_debug( "Resize: %dx%d, Window frame size: %dx%d\n", w, h, frameGeometry().width(), frameGeometry().height() ); } /* void ViewWindow::resizeEvent ( QResizeEvent * e ) { int w = e->size().width(); int h = e->size().height(); m_menuBar->move( 0, 0 ); m_menuBar->resize( w, m_menuBar->height() ); m_viewPanel->move( 0, m_menuBar->height() ); m_viewPanel->resize( w, h - m_menuBar->height() - m_statusBar->height() ); m_statusBar->move( 0, h - m_statusBar->height() ); m_statusBar->resize( w, m_statusBar->height() ); g_prefs( "ui_viewwin_width" ) = w; g_prefs( "ui_viewwin_height" ) = h; } */ void ViewWindow::helpNowEvent() { HelpWin * win = new HelpWin( "olh_mainwin.html", false ); win->show(); } void ViewWindow::contextMenuEvent( QContextMenuEvent * e ) { e->ignore(); } void ViewWindow::saveModelEvent() { const char * filename = m_model->getFilename(); if ( filename && filename[0] ) { Model::ModelErrorE err = FilterManager::getInstance()->writeFile( m_model, filename, false ); if ( err == Model::ERROR_NONE ) { m_model->setSaved( true ); prefs_recent_model( filename ); } else { m_abortQuit = true; if ( Model::operationFailed( err ) ) { QString reason = modelErrStr( err, m_model ); reason = QString(filename) + QString(":\n") + reason; msg_error( (const char *) reason.toUtf8() ); } } } else { saveModelAsEvent(); } } void ViewWindow::saveModelAsEvent() { saveModelInternal( m_model, false ); } void ViewWindow::exportModelEvent() { saveModelInternal( m_model, true ); } void ViewWindow::exportSelectedEvent() { if ( m_model->getSelectedTriangleCount() == 0 && m_model->getSelectedPointCount() == 0 && m_model->getSelectedProjectionCount() == 0 ) { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", tr( "You must have at least 1 face, joint, or point selected to Export Selected" ).toUtf8().data() ); return; } Model * m = m_model->copySelected(); if ( !m ) return; saveModelInternal( m, true ); delete m; } void ViewWindow::saveModelInternal( Model * model, bool exportModel ) { list formats = FilterManager::getInstance()->getAllWriteTypes( exportModel ); QString formatsStr = ((exportModel) ? tr( "All Exportable Formats" ) : tr( "All Writable Formats" )) + QString(" (" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += QString( " " ); } } formatsStr += QString( ")" ); const char * modelFile = model->getFilename(); QString dir = QString::fromUtf8( g_prefs( "ui_model_dir" ).stringValue().c_str() ); if ( exportModel ) { dir = QString::fromUtf8( g_prefs( "ui_export_dir" ).stringValue().c_str() ); } else { if ( modelFile && modelFile[0] != '\0' ) { std::string fullname; std::string fullpath; std::string basename; normalizePath( modelFile, fullname, fullpath, basename ); dir = tr( fullpath.c_str() ); } } if ( dir.isEmpty() ) { dir = QString( "." ); } QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; ") + tr( "All Files (*)" ) ); d.setAcceptMode( QFileDialog::AcceptSave ); if ( exportModel ) { d.selectFile( QString( model->getExportFile() ) ); } d.setWindowTitle( tr( "Save model file as" ) ); if ( exportModel ) { d.setWindowTitle( tr( "Export model" ) ); } d.selectNameFilter( formatsStr ); d.setFileMode( QFileDialog::AnyFile ); m_abortQuit = true; bool again = true; while ( again ) { again = false; if ( QDialog::Accepted == d.exec() ) { bool save = true; QStringList files = d.selectedFiles(); if ( save && !files.empty() ) { std::string filename = (const char *) files[0].toUtf8(); if ( !strchr(filename.c_str(), '.' ) ) { filename += ".mm3d"; } Model::ModelErrorE err = FilterManager::getInstance()->writeFile( model, filename.c_str(), exportModel ); if ( err == Model::ERROR_NONE ) { m_abortQuit = false; if ( exportModel ) { g_prefs( "ui_export_dir" ) = (const char *) d.directory().absolutePath().toUtf8(); model->setExportFile( filename.c_str() ); } else { model->setSaved( true ); model->setFilename( filename.c_str() ); g_prefs( "ui_model_dir" ) = (const char *) d.directory().absolutePath().toUtf8(); } prefs_recent_model( (const char *) filename.c_str() ); updateCaption(); } else { if ( Model::operationFailed( err ) ) { msg_error( modelErrStr( err, model ).toUtf8() ); } } } } } } void ViewWindow::mergeModelsEvent() { list formats = FilterManager::getInstance()->getAllReadTypes(); QString formatsStr = tr( "All Supported Formats", "model formats" ) + QString( " (" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += QString( " " ); } } formatsStr += QString(")"); QString dir = QString::fromUtf8( g_prefs( "ui_model_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = "."; } QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; ") + tr( "All Files (*)" ) ); d.setWindowTitle( tr( "Open model file" ) ); d.selectNameFilter( formatsStr ); if ( QDialog::Accepted == d.exec() ) { QStringList files = d.selectedFiles(); if ( files.empty() ) return; std::string filename = (const char *) files[0].toUtf8(); Model::ModelErrorE err; Model * model = new Model(); if ( (err = FilterManager::getInstance()->readFile( model, filename.c_str() )) == Model::ERROR_NONE) { model_show_alloc_stats(); MergeWindow mw( model, m_model, this ); if ( mw.exec() ) { Model::AnimationMergeE mode = Model::AM_NONE; if ( mw.getIncludeAnimation() ) { mode = Model::AM_ADD; if ( mw.getAnimationMerge() ) { mode = Model::AM_MERGE; } } double rot[3]; double trans[3]; mw.getRotation( rot ); mw.getTranslation( trans ); m_model->mergeModels( model, mw.getIncludeTexture(), mode, true, mw.getPoint(), trans, rot ); m_model->operationComplete( tr("Merge models").toUtf8() ); g_prefs( "ui_model_dir" ) = (const char *) d.directory().absolutePath().toUtf8(); m_viewPanel->modelUpdatedEvent(); } prefs_recent_model( filename.c_str() ); delete model; } else { if ( Model::operationFailed( err ) ) { QString reason = modelErrStr( err, model ); reason = tr(filename.c_str()) + tr(":\n") + reason; msg_error( (const char *) reason.toUtf8() ); } delete model; } } } void ViewWindow::mergeAnimationsEvent() { list formats = FilterManager::getInstance()->getAllReadTypes(); QString formatsStr = tr( "All Supported Formats", "model formats") + QString( " (" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += QString( " " ); } } formatsStr += QString( ")" ); QString dir = QString::fromUtf8( g_prefs( "ui_model_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = "."; } QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; ") + tr( "All Files (*)" ) ); d.setWindowTitle( tr( "Open model file" ) ); d.selectNameFilter( formatsStr ); if ( QDialog::Accepted == d.exec() ) { QStringList files = d.selectedFiles(); if ( files.empty() ) return; std::string filename = (const char *) files[0].toUtf8(); Model::ModelErrorE err; Model * model = new Model(); if ( (err = FilterManager::getInstance()->readFile( model, filename.c_str() )) == Model::ERROR_NONE) { model_show_alloc_stats(); m_model->mergeAnimations( model ); m_model->operationComplete( tr("Merge animations").toUtf8() ); prefs_recent_model( filename.c_str() ); delete model; } else { if ( Model::operationFailed( err ) ) { QString reason = modelErrStr( err, model ); reason = tr(filename.c_str()) + tr(":\n") + reason; msg_error( (const char *) reason.toUtf8() ); } delete model; } } } void ViewWindow::scriptEvent() { #ifdef HAVE_LUALIB QString dir = QString::fromUtf8( g_prefs( "ui_script_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = QString("."); } QString formatsStr = QString( "Lua scripts (*.lua)" ); QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; ") + tr( "All Files (*)" ) ); d.setWindowTitle( tr( "Open model file" ) ); d.selectNameFilter( formatsStr ); if ( QDialog::Accepted == d.exec() ) { QStringList files = d.selectedFiles(); if ( files.empty() ) return; g_prefs( "ui_script_dir" ) = (const char *) d.directory().absolutePath().toUtf8(); std::string filename = (const char *) files[0].toUtf8(); runScript( filename.c_str() ); } #endif // HAVE_LUALIB } void ViewWindow::runScript( const char * filename ) { #ifdef HAVE_LUALIB if ( filename ) { LuaScript lua; LuaContext lc( m_model ); luaif_registerfunctions( &lua, &lc ); std::string scriptfile = filename; std::string fullname; std::string fullpath; std::string basename; normalizePath( scriptfile.c_str(), fullname, fullpath, basename ); log_debug( "running script %s\n", basename.c_str() ); int rval = lua.runFile( scriptfile.c_str() ); prefs_recent_script( filename ); if ( rval == 0 ) { log_debug( "script complete, exited normally\n" ); QString str = tr("Script %1 complete").arg(basename.c_str()); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", (const char *) str.toUtf8() ); } else { log_error( "script complete, exited with error code %d\n", rval ); QString str = tr("Script %1 error %2") .arg(basename.c_str()) .arg(lua.error()); model_status( m_model, StatusError, STATUSTIME_LONG, "%s", (const char *) str.toUtf8() ); } m_model->setNoAnimation(); m_model->operationComplete( basename.c_str() ); m_viewPanel->modelUpdatedEvent(); } #endif // HAVE_LUALIB } void ViewWindow::closeEvent( QCloseEvent * e ) { saveDockPositions(); #ifdef CODE_DEBUG e->accept(); #else // CODE_DEBUG if ( ! m_model->getSaved() ) { int val = QMessageBox::warning( this, tr("Save first?"), tr("Model has been modified\nDo you want to save before closing?"), QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel ); switch ( val ) { case QMessageBox::Yes: m_abortQuit = false; saveModelEvent(); if ( ! m_abortQuit ) { e->accept(); } break; case QMessageBox::No: e->accept(); break; case QMessageBox::Cancel: e->ignore(); break; default: { QString str = tr( "Unknown response: %1, Canceling close request" ) .arg( val ); msg_error( (const char *) str.toUtf8() ); } e->ignore(); break; } } else { e->accept(); } #endif // CODE_DEBUG } QAction * ViewWindow::insertMenuItem( QMenu * parentMenu, bool isTool, const QString & path, const QString & name, QMenu * subMenu ) { QMenu * addMenu = parentMenu; if ( path.length() != 0 ) { bool found = false; MenuItemList::iterator it; for ( it = m_menuItems.begin(); it != m_menuItems.end(); it++ ) { if ( it->text == path ) { addMenu = it->menu; found = true; } } if ( !found ) { // TODO deal with multi-level paths addMenu = new QMenu( this ); QString module; if ( isTool ) { module = "Tool"; connect( addMenu, SIGNAL(triggered(QAction*)), this, SLOT(toolActivated(QAction*))); } else { module = "Command"; } addMenu->setTitle( qApp->translate( module.toUtf8(), path.toUtf8() ) ); parentMenu->addMenu( addMenu ); MenuItemT mi; mi.text = path; mi.menu = addMenu; m_menuItems.push_back( mi ); } } QAction * id; if ( subMenu ) { subMenu->setTitle( name ); id = addMenu->addMenu( subMenu ); } else { id = addMenu->addAction( name ); } log_debug( "added %s as id %p\n", (const char *) name.toUtf8(), id ); return id; } void ViewWindow::frameAllEvent() { double x1, y1, z1, x2, y2, z2; if ( m_model->getBoundingRegion( &x1, &y1, &z1, &x2, &y2, &z2 ) ) { m_viewPanel->frameArea( x1, y1, z1, x2, y2, z2 ); } } void ViewWindow::frameSelectedEvent() { double x1, y1, z1, x2, y2, z2; if ( m_model->getSelectedBoundingRegion( &x1, &y1, &z1, &x2, &y2, &z2 ) ) { m_viewPanel->frameArea( x1, y1, z1, x2, y2, z2 ); } } void ViewWindow::showContextEvent() { m_viewPanel->modelUpdatedEvent(); // Set model so that it is properly initialized before display m_contextPanel->setModel( m_model ); m_contextPanel->show(); m_contextPanel->raise(); // Set model again so that it actually displays properties m_contextPanel->setModel( m_model ); } void ViewWindow::renderBadEvent() { g_prefs( "ui_render_bad_textures" ) = 1; m_viewPanel->modelUpdatedEvent(); } void ViewWindow::noRenderBadEvent() { g_prefs( "ui_render_bad_textures" ) = 0; m_viewPanel->modelUpdatedEvent(); } void ViewWindow::renderBackface() { g_prefs( "ui_render_backface_cull" ) = 0; m_viewPanel->modelUpdatedEvent(); } void ViewWindow::noRenderBackface() { g_prefs( "ui_render_backface_cull" ) = 1; m_viewPanel->modelUpdatedEvent(); } void ViewWindow::renderProjections() { g_prefs( "ui_render_projections" ) = 0; m_model->setDrawProjections( true ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::noRenderProjections() { bool doHide = true; if ( m_model->getSelectedProjectionCount() > 0 ) { doHide = false; char ch = msg_info_prompt( (const char *) tr("Cannot hide with selected projections. Unselect projections now?").toUtf8(), "yN" ); if ( toupper(ch) == 'Y' ) { doHide = true; m_model->unselectAll(); m_model->operationComplete( tr("Hide projections").toUtf8() ); } } if ( doHide ) { g_prefs( "ui_render_projections" ) = 1; m_model->setDrawProjections( false ); m_viewPanel->modelUpdatedEvent(); } } void ViewWindow::renderSelectionEvent() { g_prefs( "ui_render_3d_selections" ) = 1; m_viewPanel->modelUpdatedEvent(); } void ViewWindow::noRenderSelectionEvent() { g_prefs( "ui_render_3d_selections" ) = 0; m_viewPanel->modelUpdatedEvent(); } void ViewWindow::boneJointHide() { bool doHide = true; if ( m_model->getSelectedBoneJointCount() > 0 ) { doHide = false; char ch = msg_info_prompt( (const char *) tr("Cannot hide with selected joints. Unselect joints now?").toUtf8(), "yN" ); if ( toupper(ch) == 'Y' ) { doHide = true; m_model->unselectAll(); m_model->operationComplete( tr("Hide bone joints").toUtf8() ); } } if ( doHide ) { m_model->setDrawJoints( Model::JOINTMODE_NONE ); m_viewPanel->modelUpdatedEvent(); } } void ViewWindow::boneJointLines() { Model::DrawJointModeE m = Model::JOINTMODE_LINES; g_prefs( "ui_draw_joints" ) = (int) m; m_model->setDrawJoints( m ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::boneJointBones() { Model::DrawJointModeE m = Model::JOINTMODE_BONES; g_prefs( "ui_draw_joints" ) = (int) m; m_model->setDrawJoints( m ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::viewportSettingsEvent() { ViewportSettings * win = new ViewportSettings(); win->show(); } void ViewWindow::toolActivated( QAction * id ) { log_debug( "toolActivated(%p)\n", id ); for ( ToolMenuItemList::iterator it = m_tools.begin(); it != m_tools.end(); ++it ) { if ( (*it)->id == id ) { ::Tool * tool = (*it)->tool; for ( int t = 0; t < m_toolCount; t++ ) { if ( m_toolList[t] == tool ) { if ( !m_toolButtons[t]->isChecked() ) { m_toolButtons[t]->setChecked( true ); } m_currentTool = m_toolList[ t ]; } } return; } } } void ViewWindow::scriptActivated( QAction * id ) { } void ViewWindow::groupWindowEvent() { GroupWindow * win = new GroupWindow( m_model ); win->show(); } void ViewWindow::textureWindowEvent() { TextureWindow * win = new TextureWindow( m_model ); win->show(); } void ViewWindow::groupCleanWindowEvent() { GroupCleanWin * win = new GroupCleanWin( m_model ); win->show(); } void ViewWindow::textureCoordEvent() { if ( m_model->getSelectedTriangleCount() > 0 ) { m_textureCoordWin->show(); m_textureCoordWin->raise(); } else { msg_info( (const char *) tr("You must select faces first.\nUse the 'Select Faces' tool.", "Notice that user must have faces selected to open 'edit texture coordinates' window" ).toUtf8()); } } void ViewWindow::paintTextureEvent() { if ( m_model->getSelectedTriangleCount() > 0 ) { PaintTextureWin * win = new PaintTextureWin( m_model ); win->show(); } else { msg_info( (const char *) tr("You must select faces first.\nUse the 'Select Faces' tool.", "Notice that user must have faces selected to open 'paint texture' window").toUtf8() ); } } // ContextPanelObserver method void ViewWindow::showProjectionEvent() { projectionWindowEvent(); } void ViewWindow::projectionWindowEvent() { m_projectionWin->show(); m_projectionWin->raise(); } void ViewWindow::transformWindowEvent() { m_transformWin->show(); m_transformWin->raise(); } void ViewWindow::metaWindowEvent() { MetaWindow * win = new MetaWindow( m_model ); win->show(); } void ViewWindow::boolWindowEvent() { m_boolPanel->show(); m_boolPanel->raise(); } void ViewWindow::reloadTexturesEvent() { if( TextureManager::getInstance()->reloadTextures() ) { invalidateModelTextures(); } } void ViewWindow::editDisableEvent() { m_canEdit = false; m_viewPanel->setEnabled( m_canEdit ); } void ViewWindow::editEnableEvent() { m_canEdit = true; m_viewPanel->setEnabled( m_canEdit ); } void ViewWindow::undoRequest() { log_debug( "undo request\n" ); if ( m_model->canUndo() ) { const char * opname = m_model->getUndoOpName(); if ( m_animWin->isVisible() ) { m_animWidget->undoRequest(); } else { m_model->undo(); if ( m_model->getAnimationMode() ) { m_animWidget->initialize( m_model, true ); animationModeOn(); m_animWin->show(); } else { m_viewPanel->modelUpdatedEvent(); } } QString str = tr( "Undo %1" ).arg( (opname && opname[0]) ? opname : "" ); model_status ( m_model, StatusNormal, STATUSTIME_SHORT, "%s", (const char *) str.toUtf8() ); if ( m_model->getSelectedBoneJointCount() > 0 ) { m_model->setDrawJoints( (Model::DrawJointModeE) g_prefs( "ui_draw_joints" ).intValue() ); m_viewPanel->modelUpdatedEvent(); } } else { model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Nothing to undo").toUtf8().data() ); } } void ViewWindow::redoRequest() { log_debug( "redo request\n" ); if ( m_model->canRedo() ) { const char * opname = m_model->getRedoOpName(); if ( m_animWin->isVisible() ) { m_animWidget->redoRequest(); } else { m_model->redo(); if ( m_model->getAnimationMode() ) { m_animWidget->initialize( m_model, true ); animationModeOn(); m_animWin->show(); } else { m_viewPanel->modelUpdatedEvent(); } } if ( m_model->getSelectedBoneJointCount() > 0 ) { m_model->setDrawJoints( (Model::DrawJointModeE) g_prefs( "ui_draw_joints" ).intValue() ); m_viewPanel->modelUpdatedEvent(); } QString str = tr( "Redo %1" ).arg( (opname && opname[0]) ? opname : "" ); model_status ( m_model, StatusNormal, STATUSTIME_SHORT, "%s", (const char *) str.toUtf8() ); } else { model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", tr("Nothing to redo").toUtf8().data() ); } } void ViewWindow::snapToSelectedEvent( QAction * snapTo ) { log_debug( "snapToSelectedEvent( %p )\n", snapTo ); g_prefs( "ui_snap_grid" ) = ( m_snapToGrid->isChecked() ) ? 1 : 0; g_prefs( "ui_snap_vertex" ) = ( m_snapToVertex->isChecked() ) ? 1 : 0; } void ViewWindow::helpWindowEvent() { HelpWin * win = new HelpWin(); win->show(); } void ViewWindow::aboutWindowEvent() { AboutWin * win = new AboutWin(); win->show(); } void ViewWindow::licenseWindowEvent() { LicenseWin * win = new LicenseWin(); win->show(); } void ViewWindow::animSetWindowEvent() { if ( m_animWin->isVisible() ) { stopAnimationMode(); } AnimSetWindow asw( m_model, this ); asw.exec(); } void ViewWindow::animExportWindowEvent() { if ( m_model->getAnimCount( Model::ANIMMODE_SKELETAL ) > 0 || m_model->getAnimCount( Model::ANIMMODE_FRAME ) > 0 ) { AnimExportWindow aew( m_model, m_viewPanel, this ); aew.exec(); } else { msg_error( (const char *) tr("This model does not have any animations").toUtf8() ); } } void ViewWindow::animSetRotEvent() { double point[3] = { 0.0, 0.0, 0.0 }; Matrix m; m.loadIdentity(); m_model->rotateSelected( m, point ); m_model->operationComplete( tr("Set rotation keframe").toUtf8() ); } void ViewWindow::animSetTransEvent() { Matrix m; m.loadIdentity(); m_model->translateSelected( m ); m_model->operationComplete( tr("Set translation keframe").toUtf8() ); } void ViewWindow::animClearRotEvent() { if ( m_model->getAnimationMode() == Model::ANIMMODE_SKELETAL ) { bool isRotation = true; list joints; m_model->getSelectedBoneJoints( joints ); for ( list::iterator it = joints.begin(); it != joints.end(); it++ ) { m_model->deleteSkelAnimKeyframe( m_model->getCurrentAnimation(), m_model->getCurrentAnimationFrame(), *it, isRotation ); } } m_model->operationComplete( tr("Clear rotation keframe").toUtf8() ); } void ViewWindow::animClearTransEvent() { if ( m_model->getAnimationMode() == Model::ANIMMODE_SKELETAL ) { bool isRotation = false; list joints; m_model->getSelectedBoneJoints( joints ); for ( list::iterator it = joints.begin(); it != joints.end(); it++ ) { m_model->deleteSkelAnimKeyframe( m_model->getCurrentAnimation(), m_model->getCurrentAnimationFrame(), *it, isRotation ); } } m_model->operationComplete( tr("Clear translation keframe").toUtf8() ); } void ViewWindow::animCopyFrameEvent() { if ( m_animWin->isVisible() ) { m_animPasteFrame->setEnabled( false ); m_animPasteSelected->setEnabled( false ); if ( m_animWidget->copyFrame( false ) ) { m_animPasteFrame->setEnabled( true ); } } } void ViewWindow::animPasteFrameEvent() { if ( m_animWin->isVisible() ) { m_animWidget->pasteFrame(); } } void ViewWindow::animCopySelectedEvent() { if ( m_animWin->isVisible() ) { m_animPasteFrame->setEnabled( false ); m_animPasteSelected->setEnabled( false ); if ( m_animWidget->copyFrame( true ) ) { m_animPasteSelected->setEnabled( true ); } } } void ViewWindow::animPasteSelectedEvent() { animPasteFrameEvent(); // Same logic for both } void ViewWindow::animClearFrameEvent() { if ( m_animWin->isVisible() ) { m_animWidget->clearFrame(); } } void ViewWindow::startAnimationMode() { m_animWidget->initialize( m_model, false ); animationModeOn(); m_animWin->show(); } void ViewWindow::stopAnimationMode() { m_animWin->close(); m_animWidget->stopAnimationMode(); animationModeOff(); } void ViewWindow::animationModeOn() { m_animExportItem->setEnabled( false ); m_startAnimItem->setEnabled( false ); m_stopAnimItem->setEnabled( true ); m_animSetRotItem->setEnabled( true ); m_animSetTransItem->setEnabled( true ); m_animClearRotItem->setEnabled( true ); m_animClearTransItem->setEnabled( true ); m_animCopyFrame->setEnabled( true ); m_animPasteFrame->setEnabled( false ); // Disabled until copy occurs m_animClearFrame->setEnabled( true ); m_animCopySelected->setEnabled( true ); m_animPasteSelected->setEnabled( false ); // Disabled until copy occurs } void ViewWindow::animationModeOff() { m_model->setNoAnimation(); m_viewPanel->modelUpdatedEvent(); m_animWin->close(); m_animExportItem->setEnabled( true ); m_startAnimItem->setEnabled( true ); m_stopAnimItem->setEnabled( false ); m_animSetRotItem->setEnabled( false ); m_animSetTransItem->setEnabled( false ); m_animClearRotItem->setEnabled( false ); m_animClearTransItem->setEnabled( false ); m_animCopyFrame->setEnabled( false ); m_animPasteFrame->setEnabled( false ); m_animClearFrame->setEnabled( false ); m_animCopySelected->setEnabled( false ); m_animPasteSelected->setEnabled( false ); editEnableEvent(); } void ViewWindow::contextPanelHidden() { m_showContext->setText( tr( "Show Properties", "View|Show Properties") ); //m_viewPanel->modelUpdatedEvent(); } void ViewWindow::buttonToggled( bool on ) { if ( on ) { if ( m_currentTool ) m_currentTool->deactivated(); for ( int t = 0; t < m_toolCount; t++ ) { if ( m_toolButtons[t]->isChecked() ) { if ( m_last != m_toolButtons[t] ) { m_currentTool = m_toolList[ t ]; m_currentTool->activated( 0, m_model, this ); m_toolbox->setCurrentTool( m_currentTool ); break; } } } } } static void _registerKeyBinding( ::Tool * tool, int index, QMenu * menu, QAction * id ) { QString name = QString( "tool_" ) + QString::fromUtf8( tool->getName( 0 ) ); if ( index > 0 ) { name += QString( "_" ) + QString::fromUtf8( tool->getName( index ) ); } name = name.replace( QString("."), QString("") ); name = name.replace( QString(" "), QString("_") ); name = name.toLower(); QKeySequence key = g_keyConfig.getKey( (const char *) name.toUtf8() ); if ( !key.isEmpty() ) { id->setShortcut( key ); } } static QString _makeToolTip( ::Tool * tool, int index ) { QString lookupStr = QString( "tool_" ) + QString::fromUtf8( tool->getName( 0 ) ); if ( index > 0 ) { lookupStr += QString( "_" ) + QString::fromUtf8( tool->getName( index ) ); } lookupStr = lookupStr.replace( QString("."), QString("") ); lookupStr = lookupStr.replace( QString(" "), QString("_") ); lookupStr = lookupStr.toLower(); QKeySequence key = g_keyConfig.getKey( (const char *) lookupStr.toUtf8() ); QString name = qApp->translate( "Tool", tool->getName( index ) ); if ( !key.isEmpty() ) { name += QString(" ("); name += key.toString(); name += QString(")"); } return name; } void ViewWindow::initializeToolbox() { connect( m_toolMenu, SIGNAL(triggered(QAction*)), this, SLOT(toolActivated(QAction*))); m_toolbox->registerAllTools(); QActionGroup * grp = new QActionGroup( this ); m_toolButtons = new QActionPtr[ m_toolbox->getToolCount() ]; m_toolList = new ToolPtr[ m_toolbox->getToolCount() ]; m_toolCount = 0; ::Tool * tool = m_toolbox->getFirstTool(); while ( tool ) { if ( !tool->isSeparator() ) { ToolMenuItemT * item; QAction * id; int count = tool->getToolCount(); if ( count > 1 ) { QMenu * menu = new QMenu( this ); connect( menu, SIGNAL(triggered(QAction*)), this, SLOT(toolActivated(QAction*))); for ( int t = 1; t < count; t++ ) { const char * name = tool->getName( t ); id = menu->addAction( qApp->translate( "Tool", name ) ); _registerKeyBinding( tool, t, menu, id ); item = new ToolMenuItemT; item->id = id; item->tool = tool; item->arg = t; m_tools.push_back( item ); // Create tool button QIcon set; set.addPixmap( QPixmap( tool->getPixmap() ) ); m_toolList[m_toolCount] = tool; // Text below m_toolButtons[ m_toolCount ] = m_toolBar->addAction( set, qApp->translate( "Tool", tool->getName(t) ) ); m_toolButtons[ m_toolCount ]->setCheckable( true ); if ( name && name[0] ) { m_toolButtons[ m_toolCount ]->setToolTip( _makeToolTip( tool, t ) ); } connect( m_toolButtons[m_toolCount], SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool))); grp->addAction( m_toolButtons[ m_toolCount ] ); m_toolCount++; } //id = m_toolMenu->addAction( qApp->translate( "Tool", tool->getName(0)), menu ); id = insertMenuItem( m_toolMenu, true, tool->getPath(), qApp->translate( "Tool", tool->getName(0)), menu ); _registerKeyBinding( tool, 0, m_toolMenu, id ); item = new ToolMenuItemT; item->id = id; item->tool = tool; item->arg = 0; m_tools.push_back( item ); } else { const char * name = tool->getName( 0 ); //id = m_toolMenu->addAction( qApp->translate( "Tool", name ) ); id = insertMenuItem( m_toolMenu, true, tool->getPath(), qApp->translate( "Tool", tool->getName(0)), NULL ); _registerKeyBinding( tool, 0, m_toolMenu, id ); item = new ToolMenuItemT; item->id = id; item->tool = tool; item->arg = 0; m_tools.push_back( item ); // Create tool button QIcon set; set.addPixmap( QPixmap( tool->getPixmap() ) ); m_toolList[m_toolCount] = tool; // Text below m_toolButtons[ m_toolCount ] = m_toolBar->addAction( set, qApp->translate( "Tool", tool->getName(0) ) ); m_toolButtons[ m_toolCount ]->setCheckable( true ); if ( name && name[0] ) { m_toolButtons[ m_toolCount ]->setToolTip( _makeToolTip( tool, 0 ) ); } connect( m_toolButtons[m_toolCount], SIGNAL(toggled(bool)), this, SLOT(buttonToggled(bool))); grp->addAction( m_toolButtons[ m_toolCount ] ); m_toolCount++; } } else { m_toolMenu->addSeparator(); } tool = m_toolbox->getNextTool(); } } static void _registerKeyBinding( Command * cmd, int index, QMenu * menu, QAction * id ) { QString name = QString( "cmd_" ) + QString( cmd->getName( 0 ) ); if ( index > 0 ) { name += QString( "_" ) + QString( cmd->getName( index ) ); } name = name.replace( QString("."), QString("") ); name = name.replace( QString(" "), QString("_") ); name = name.toLower(); QKeySequence key = g_keyConfig.getKey( (const char *) name.toUtf8() ); if ( !key.isEmpty() ) { id->setShortcut( key ); } } void ViewWindow::initializeCommands() { //m_cmdMgr = new CommandManager(); //init_std_cmds( m_cmdMgr ); m_cmdMgr = CommandManager::getInstance(); Command * cmd = m_cmdMgr->getFirstCommand(); while ( cmd ) { if ( !cmd->isSeparator() ) { CommandMenuItemT * item; QAction * id; int count = cmd->getCommandCount(); if ( count > 1 ) { QMenu * menu = new QMenu( this ); for ( int t = 1; t < count; t++ ) { id = menu->addAction( qApp->translate( "Command", cmd->getName(t) ) ); item = new CommandMenuItemT; item->id = id; item->command = cmd; item->arg = t; item->widget = new CommandWidget(this, m_model, &m_canEdit, cmd, t ); connect(id, SIGNAL(triggered(bool)), item->widget, SLOT(activateCommand(bool))); _registerKeyBinding( cmd, t, menu, id ); m_primitiveCommands.push_back( item ); } log_debug( "adding command '%s' to menus\n", cmd->getName(0) ); id = insertMenuItem( m_geometryMenu, false, cmd->getPath(), qApp->translate( "Command", cmd->getName(0) ), menu ); //id = m_geometryMenu->addAction( qApp->translate( "Command", cmd->getName(0) ), menu ); _registerKeyBinding( cmd, 0, m_geometryMenu, id ); item = new CommandMenuItemT; item->id = id; item->command = cmd; item->arg = 0; item->widget = new CommandWidget(this, m_model, &m_canEdit, cmd, 0 ); connect(id, SIGNAL(triggered(bool)), item->widget, SLOT(activateCommand(bool))); m_primitiveCommands.push_back( item ); } else { QMenu * curMenu = m_geometryMenu; id = insertMenuItem( m_geometryMenu, false, cmd->getPath(), qApp->translate( "Command", cmd->getName(0)), NULL ); //id = curMenu->addAction( qApp->translate( "Command", cmd->getName(0)) ); item = new CommandMenuItemT; item->id = id; item->command = cmd; item->arg = 0; item->widget = new CommandWidget(this, m_model, &m_canEdit, cmd, 0 ); connect(id, SIGNAL(triggered(bool)), item->widget, SLOT(activateCommand(bool))); _registerKeyBinding( cmd, 0, curMenu, id ); log_debug( "adding command '%s' to menus\n", cmd->getName(0) ); m_primitiveCommands.push_back( item ); } } else { m_geometryMenu->addSeparator(); } cmd = m_cmdMgr->getNextCommand(); } } void ViewWindow::fillMruMenu() { m_mruMenu->clear(); for ( unsigned i = 0; i < g_prefs("mru").count(); i++ ) { m_mruMenu->addAction( QDir::toNativeSeparators( QString::fromUtf8( g_prefs("mru")[i].stringValue().c_str() ) ) ); } } void ViewWindow::openMru( QAction * id ) { openModelInWindow( QDir::fromNativeSeparators( id->text() ).toUtf8() ); } void ViewWindow::fillScriptMruMenu() { m_scriptMruMenu->clear(); for ( unsigned i = 0; i < g_prefs("script_mru").count(); i++ ) { m_scriptMruMenu->addAction( QDir::toNativeSeparators( QString::fromUtf8( g_prefs("script_mru")[i].stringValue().c_str() ) ) ); } } void ViewWindow::openScriptMru( QAction * id ) { runScript( QDir::fromNativeSeparators( id->text() ).toUtf8() ); } void ViewWindow::openModelEvent() { openModelDialogInWindow(); } bool ViewWindow::openModel( const char * filename ) { bool opened = false; log_debug( " file: %s\n", filename ); Model::ModelErrorE err; Model * model = new Model(); if ( (err = FilterManager::getInstance()->readFile( model, filename )) == Model::ERROR_NONE) { opened = true; model_show_alloc_stats(); model->setSaved( true ); ViewWindow * win = new ViewWindow( model, NULL ); win->getSaved(); // Just so I don't have a warning prefs_recent_model( filename ); } else { if ( Model::operationFailed( err ) ) { QString reason = modelErrStr( err, model ); reason = QString(filename) + QString(":\n") + reason; msg_error( (const char *) reason.toUtf8() ); } delete model; } return opened; } bool ViewWindow::openModelDialog( const char * openDirectory ) { bool opened = false; list formats = FilterManager::getInstance()->getAllReadTypes(); QString formatsStr = tr( "All Supported Formats" ) + QString( " (" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += QString(" "); } } formatsStr += QString(")"); QString dir = QString::fromUtf8( g_prefs( "ui_model_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = QString( "." ); } if ( openDirectory ) { dir = QString::fromUtf8( openDirectory ); } QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; ") + tr( "All Files (*)" ) ); d.setWindowTitle( tr( "Open model file" ) ); d.selectNameFilter( formatsStr ); if ( QDialog::Accepted == d.exec() ) { QStringList files = d.selectedFiles(); if ( files.empty() ) return false; if ( openModel( files[0].toUtf8() ) ) { opened = true; g_prefs( "ui_model_dir" ) = (const char *) d.directory().absolutePath().toUtf8(); } } return opened; } bool ViewWindow::openModelInWindow( const char * filename ) { bool opened = false; log_debug( " file: %s\n", filename ); #ifndef CODE_DEBUG if ( ! m_model->getSaved() ) { int val = QMessageBox::warning( this, tr("Save first?"), tr("Model has been modified\nDo you want to save before closing?"), QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel ); switch ( val ) { case QMessageBox::Yes: m_abortQuit = false; saveModelEvent(); if ( m_abortQuit ) { return false; } break; case QMessageBox::No: break; case QMessageBox::Cancel: return false; break; default: { msg_error( (const char *) tr("Unknown response: Canceling operation").toUtf8() ); } return false; } } #endif // CODE_DEBUG Model::ModelErrorE err; Model * model = new Model(); if ( (err = FilterManager::getInstance()->readFile( model, filename )) == Model::ERROR_NONE) { opened = true; model_show_alloc_stats(); Model * oldModel = m_model; setModel( model ); delete oldModel; frameAllEvent(); prefs_recent_model( filename ); } else { if ( Model::operationFailed( err ) ) { QString reason = modelErrStr( err, model ); reason = QString(filename) + QString(":\n") + reason; msg_error( (const char *) reason.toUtf8() ); } delete model; } return opened; } bool ViewWindow::openModelDialogInWindow( const char * openDirectory ) { bool opened = false; list formats = FilterManager::getInstance()->getAllReadTypes(); QString formatsStr = tr( "All Supported Formats" ) + QString( " (" ); list::iterator it = formats.begin(); while( it != formats.end() ) { formatsStr += QString( (*it).c_str() ); it++; if ( it != formats.end() ) { formatsStr += QString(" "); } } formatsStr += QString(")"); QString dir = QString::fromUtf8( g_prefs( "ui_model_dir" ).stringValue().c_str() ); if ( dir.isEmpty() ) { dir = "."; } if ( openDirectory ) { dir = openDirectory; } QFileDialog d(NULL, QString(""), dir, formatsStr + QString(";; ") + tr( "All Files (*)" ) ); d.setWindowTitle( tr( "Open model file" ) ); d.selectNameFilter( formatsStr ); if ( QDialog::Accepted == d.exec() ) { QStringList files = d.selectedFiles(); if ( files.empty() ) return false; if ( openModelInWindow( files[0].toUtf8() ) ) { opened = true; g_prefs( "ui_model_dir" ) = (const char *) d.directory().absolutePath().toUtf8(); } } return opened; } void ViewWindow::invalidateModelTextures() { ViewWindowList::iterator windowIter; Model * model; DecalManager * mgr = DecalManager::getInstance(); for( windowIter = _winList.begin(); windowIter != _winList.end(); windowIter++ ) { model = (*windowIter)->getModel(); model->invalidateTextures(); mgr->modelUpdated( model ); } } void ViewWindow::quitEvent() { saveDockPositions(); if ( ViewWindow::closeAllWindows() ) { qApp->quit(); } } void ViewWindow::pluginWindowEvent() { // PluginWindow will delete itself PluginWindow * pluginWin = new PluginWindow(); pluginWin->show(); } void ViewWindow::backgroundWindowEvent() { // BackgroundWin will delete itself BackgroundWin * win = new BackgroundWin( m_model, m_viewPanel ); win->show(); } void ViewWindow::newModelEvent() { ViewWindow * win = new ViewWindow( new Model, NULL ); win->getSaved(); // Just so I don't have a warning } void ViewWindow::savedTimeoutCheck() { updateCaption(); } void ViewWindow::saveDockPositions() { QString dockFile( getMm3dHomeDirectory().c_str() ); dockFile += "/"; dockFile += DOCK_FILENAME; QFile fp( dockFile ); if ( fp.open( QIODevice::WriteOnly ) ) { { // Must go out of scope before fp is closed. QDataStream stream(&fp); stream << this->saveState( DOCK_VERSION ); } fp.close(); } } void ViewWindow::loadDockPositions() { QString dockFile( getMm3dHomeDirectory().c_str() ); dockFile += "/"; dockFile += DOCK_FILENAME; QFile fp( dockFile ); if ( fp.open( QIODevice::ReadOnly ) ) { { // Must go out of scope before fp is closed. QDataStream stream(&fp); QByteArray state; stream >> state; this->restoreState( state, DOCK_VERSION ); } fp.close(); } } void ViewWindow::updateCaption() { QString caption = QString( "Maverick Model 3D: " ); if ( m_model ) { caption += m_model->getSaved() ? QString("") : QString("* "); const char * filename = m_model->getFilename(); if ( filename && filename[0] ) { std::string fullName; std::string fullPath; std::string baseName; normalizePath( filename, fullName, fullPath, baseName ); caption += baseName.c_str(); } else { caption += tr( "[unnamed]", "For filename in title bar (if not set)" ); } } setWindowTitle( caption ); } mm3d-1.3.15/src/implui/viewwin.h000066400000000000000000000231051466047437300164070ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __VIEWWIN_H #define __VIEWWIN_H #include "config.h" #include "contextpanelobserver.h" #include using std::list; class QVBoxLayout; class QMenuBar; class QMenu; class QToolBar; class QTimer; class ViewPanel; class ContextPanel; class BoolPanel; class ProjectionWin; class TextureCoord; class TransformWindow; class StatusBar; class Model; class Command; class Tool; class Script; class AnimWidget; class AnimWindow; class QToolButton; class Toolbox; class CommandManager; class QContextMenuEvent; class QCloseEvent; class QResizeEvent; #include #include class CommandWidget : public QObject { Q_OBJECT public: CommandWidget( QObject * parent, Model * model, bool * canEdit, Command * cmd, int index ); virtual ~CommandWidget(); public slots: void setModel( Model * m ); void activateCommand( bool ); private: Model * m_model; bool * m_canEdit; Command * m_cmd; int m_index; }; class ViewWindow : public QMainWindow, public ContextPanelObserver { Q_OBJECT public: ViewWindow( Model * model, QWidget * parent = NULL ); virtual ~ViewWindow(); static bool closeAllWindows(); static bool openModelInEmptyWindow( const char * filename ); static bool openModel( const char * filename ); static bool openModelDialog( const char * openDirectory = NULL ); static void invalidateModelTextures(); void setModel( Model * model ); bool openModelInWindow( const char * filename ); bool openModelDialogInWindow( const char * openDirectory = NULL ); bool emptyWindow(); bool getSaved(); bool getAbortQuit() { return m_abortQuit; }; void setAbortQuit( bool o ) { m_abortQuit = o; }; Model *getModel() { return m_model; }; // ContextPanelObserver methods void showProjectionEvent(); signals: void modelChanged( Model * m ); public slots: void helpNowEvent(); void saveModelEvent(); void saveModelAsEvent(); void exportModelEvent(); void exportSelectedEvent(); void mergeModelsEvent(); void mergeAnimationsEvent(); void scriptEvent(); void runScript( const char * filename ); void frameAllEvent(); void frameSelectedEvent(); void showContextEvent(); void renderBadEvent(); void noRenderBadEvent(); void renderSelectionEvent(); void noRenderSelectionEvent(); void renderBackface(); void noRenderBackface(); void renderProjections(); void noRenderProjections(); void boneJointHide(); void boneJointLines(); void boneJointBones(); void viewportSettingsEvent(); void groupWindowEvent(); void textureWindowEvent(); void groupCleanWindowEvent(); void textureCoordEvent(); void paintTextureEvent(); void projectionWindowEvent(); void transformWindowEvent(); void metaWindowEvent(); void boolWindowEvent(); void reloadTexturesEvent(); void buttonToggled( bool on ); void toolActivated( QAction * id ); void scriptActivated( QAction * id ); void animSetWindowEvent(); void animExportWindowEvent(); void animSetRotEvent(); void animSetTransEvent(); void animClearRotEvent(); void animClearTransEvent(); void animCopyFrameEvent(); void animPasteFrameEvent(); void animClearFrameEvent(); void animCopySelectedEvent(); void animPasteSelectedEvent(); void startAnimationMode(); void stopAnimationMode(); void animationModeOn(); void animationModeOff(); void contextPanelHidden(); void editDisableEvent(); void editEnableEvent(); void undoRequest(); void redoRequest(); void snapToSelectedEvent( QAction * snapTo ); void fillMruMenu(); void openMru( QAction * id ); void fillScriptMruMenu(); void openScriptMru( QAction * id ); void openModelEvent(); void newModelEvent(); void quitEvent(); void pluginWindowEvent(); void backgroundWindowEvent(); void helpWindowEvent(); void aboutWindowEvent(); void licenseWindowEvent(); void savedTimeoutCheck(); // influences slots void jointWinEvent(); void jointAssignSelectedToJoint(); void jointAutoAssignSelected(); void jointRemoveInfluencesFromSelected(); void jointRemoveInfluenceJoint(); void jointMakeSingleInfluence(); void jointSelectInfluenceJoints(); void jointSelectInfluencedVertices(); void jointSelectInfluencedPoints(); void jointSelectUnassignedVertices(); void jointSelectUnassignedPoints(); protected: void saveModelInternal( Model * model, bool exportModel = false ); void contextMenuEvent( QContextMenuEvent * e ); void saveDockPositions(); void loadDockPositions(); void updateCaption(); void initializeToolbox(); void initializeCommands(); void closeEvent( QCloseEvent * e ); void resizeEvent( QResizeEvent * ); // returns id in menu QAction * insertMenuItem( QMenu * parentMenu, bool isTool, const QString & path, const QString & name, QMenu * subMenu ); struct _ToolMenuItem_t { QAction * id; ::Tool * tool; int arg; }; typedef struct _ToolMenuItem_t ToolMenuItemT; typedef list< ToolMenuItemT * > ToolMenuItemList; typedef struct _CommandMenuItem_t { QAction * id; Command * command; CommandWidget * widget; int arg; } CommandMenuItemT; typedef list< CommandMenuItemT * > CommandMenuItemList; typedef struct _MenuItem_t { QString text; QMenu * menu; } MenuItemT; typedef list< MenuItemT > MenuItemList; QMenuBar * m_menuBar; QMenu * m_fileMenu; QMenu * m_viewMenu; QMenu * m_renderMenu; QMenu * m_toolMenu; QMenu * m_modelMenu; QMenu * m_geometryMenu; QMenu * m_materialsMenu; QMenu * m_jointsMenu; QMenu * m_animMenu; QMenu * m_scriptMenu; QMenu * m_helpMenu; QMenu * m_mruMenu; QMenu * m_scriptMruMenu; QMenu * m_snapMenu; QToolBar * m_toolBar; ViewPanel * m_viewPanel; ContextPanel * m_contextPanel; BoolPanel * m_boolPanel; ProjectionWin * m_projectionWin; TextureCoord * m_textureCoordWin; TransformWindow * m_transformWin; StatusBar * m_statusBar; Model * m_model; AnimWindow * m_animWin; AnimWidget * m_animWidget; QAction * m_snapToGrid; QAction * m_snapToVertex; QAction * m_animSetsItem; QAction * m_animExportItem; QAction * m_animSetRotItem; QAction * m_animSetTransItem; QAction * m_animClearRotItem; QAction * m_animClearTransItem; QAction * m_animCopyFrame; QAction * m_animPasteFrame; QAction * m_animCopySelected; QAction * m_animPasteSelected; QAction * m_animClearFrame; QAction * m_startAnimItem; QAction * m_stopAnimItem; QAction * m_showContext; QAction * m_3dWire; QAction * m_3dFlat; QAction * m_3dSmooth; QAction * m_3dTexture; QAction * m_3dAlpha; QAction * m_canvasWire; QAction * m_canvasFlat; QAction * m_canvasSmooth; QAction * m_canvasTexture; QAction * m_canvasAlpha; QAction * m_view1; QAction * m_view1x2; QAction * m_view2x1; QAction * m_view2x2; QAction * m_view2x3; QAction * m_view3x2; QAction * m_view3x3; QAction * m_renderBadItem; QAction * m_noRenderBadItem; QAction * m_renderSelectionItem; QAction * m_noRenderSelectionItem; QAction * m_hideJointsItem; QAction * m_drawJointLinesItem; QAction * m_drawJointBonesItem; QAction * m_renderBackface; QAction * m_noRenderBackface; QAction * m_renderProjections; QAction * m_noRenderProjections; bool m_abortQuit; CommandMenuItemList m_primitiveCommands; CommandMenuItemList m_groupCommands; ToolMenuItemList m_tools; MenuItemList m_menuItems; CommandManager * m_cmdMgr; // Moved from toolwidget int m_toolCount; Toolbox * m_toolbox; ::Tool ** m_toolList; QAction ** m_toolButtons; QAction * m_last; ::Tool * m_currentTool; bool m_canEdit; QTimer * m_savedTimer; }; #endif // __VIEWWIN_H mm3d-1.3.15/src/implui/viewwin_influences.cc000066400000000000000000000221061466047437300207600ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "viewwin.h" #include "viewpanel.h" #include "model.h" #include "modelstatus.h" #include "jointwin.h" #include "autoassignjointwin.h" #include "log.h" void ViewWindow::jointWinEvent() { // this calls operationComplete and updates the model itself JointWin * win = new JointWin( m_model ); win->show(); } void ViewWindow::jointAssignSelectedToJoint() { std::list j; std::list::iterator jit; m_model->getSelectedBoneJoints( j ); if ( !j.empty() ) { list::iterator it; list vertList; m_model->getSelectedVertices( vertList ); list pointList; m_model->getSelectedPoints( pointList ); log_debug( "assigning %" PORTuSIZE " vertices and %" PORTuSIZE " points to joints\n", vertList.size(), pointList.size() ); QString str = tr( "Assigning %1 vertices and %2 points to joints").arg(vertList.size()).arg( pointList.size() ); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", (const char *) str.toUtf8() ); for ( it = vertList.begin(); it != vertList.end(); it++ ) { for ( jit = j.begin(); jit != j.end(); jit++ ) { double w = m_model->calculateVertexInfluenceWeight( *it, *jit ); m_model->addVertexInfluence( *it, *jit, Model::IT_Auto, w ); } } for ( it = pointList.begin(); it != pointList.end(); it++ ) { for ( jit = j.begin(); jit != j.end(); jit++ ) { double w = m_model->calculatePointInfluenceWeight( *it, *jit ); m_model->addPointInfluence( *it, *jit, Model::IT_Auto, w ); } } m_model->operationComplete( tr("Assign Selected to Joint").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } else { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", tr("You must have at least one bone joint selected.").toUtf8().data() ); } } void ViewWindow::jointAutoAssignSelected() { std::list p; std::list::iterator pit; m_model->getSelectedPositions( p ); if ( !p.empty() ) { AutoAssignJointWin win( m_model, this ); if ( win.exec() ) { double sensitivity = ((double) win.getSensitivity() ) / 100.0; bool selected = win.getSelected(); log_debug( "auto-assigning %" PORTuSIZE " vertices and points to joints\n", p.size() ); for ( pit = p.begin(); pit != p.end(); pit++ ) { m_model->autoSetPositionInfluences( *pit, sensitivity, selected ); } m_model->operationComplete( tr("Auto-Assign Selected to Bone Joints").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } } else { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", tr("You must have at least one vertex or point selected.").toUtf8().data() ); } } void ViewWindow::jointRemoveInfluencesFromSelected() { std::list< Model::Position > posList; std::list< Model::Position >::iterator it; m_model->getSelectedPositions( posList ); for ( it = posList.begin(); it != posList.end(); it++ ) { m_model->removeAllPositionInfluences( *it ); } m_model->operationComplete( tr("Remove All Influences from Selected").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointRemoveInfluenceJoint() { std::list jointList; std::list::iterator it; unsigned vcount = m_model->getVertexCount(); unsigned pcount = m_model->getPointCount(); m_model->getSelectedBoneJoints( jointList ); for ( it = jointList.begin(); it != jointList.end(); it++ ) { for ( unsigned v = 0; v < vcount; v++ ) { m_model->removeVertexInfluence( v, *it ); } for ( unsigned p = 0; p < pcount; p++ ) { m_model->removePointInfluence( p, *it ); } } m_model->operationComplete( tr("Remove Joint from Influencing").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointMakeSingleInfluence() { unsigned vcount = m_model->getVertexCount(); unsigned pcount = m_model->getPointCount(); int bcount = m_model->getBoneJointCount(); for ( unsigned v = 0; v < vcount; v++ ) { int joint = m_model->getPrimaryVertexInfluence( v ); if ( joint >= 0 ) { for ( int b = 0; b < bcount; b++ ) { if ( b != joint ) { m_model->removeVertexInfluence( v, b ); } } } } for ( unsigned p = 0; p < pcount; p++ ) { int joint = m_model->getPrimaryPointInfluence( p ); if ( joint >= 0 ) { for ( int b = 0; b < bcount; b++ ) { if ( b != joint ) { m_model->removePointInfluence( p, b ); } } } } m_model->operationComplete( tr("Convert To Single Influence").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointSelectUnassignedVertices() { m_model->unselectAllVertices(); m_model->beginSelectionDifference(); unsigned vcount = m_model->getVertexCount(); for ( unsigned v = 0; v < vcount; v++ ) { Model::InfluenceList l; m_model->getVertexInfluences( v, l ); if ( l.empty() ) { m_model->selectVertex( v ); } } m_model->endSelectionDifference(); m_model->operationComplete( tr("Select Unassigned Vertices").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointSelectUnassignedPoints() { m_model->unselectAllVertices(); m_model->beginSelectionDifference(); unsigned pcount = m_model->getPointCount(); for ( unsigned p = 0; p < pcount; p++ ) { Model::InfluenceList l; m_model->getPointInfluences( p, l ); if ( l.empty() ) { m_model->selectPoint( p ); } } m_model->endSelectionDifference(); m_model->operationComplete( tr("Select Unassigned Points").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointSelectInfluenceJoints() { m_model->beginSelectionDifference(); Model::InfluenceList ilist; Model::InfluenceList::iterator iit; list< Model::Position > posList; list< Model::Position >::iterator it; m_model->getSelectedPositions( posList ); for ( it = posList.begin(); it != posList.end(); it++ ) { m_model->getPositionInfluences( *it, ilist ); for ( iit = ilist.begin(); iit != ilist.end(); iit++ ) { m_model->selectBoneJoint( (*iit).m_boneId ); } } m_model->endSelectionDifference(); m_model->operationComplete( tr("Select Joint Influences").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointSelectInfluencedVertices() { m_model->beginSelectionDifference(); Model::InfluenceList ilist; Model::InfluenceList::iterator iit; list< int > jointList; list< int >::iterator it; unsigned vcount = m_model->getVertexCount(); m_model->getSelectedBoneJoints( jointList ); for ( it = jointList.begin(); it != jointList.end(); it++ ) { for ( unsigned v = 0; v < vcount; v++ ) { m_model->getVertexInfluences( v, ilist ); for ( iit = ilist.begin(); iit != ilist.end(); iit++ ) { if ( (*iit).m_boneId == *it ) { m_model->selectVertex( v ); } } } } m_model->endSelectionDifference(); m_model->operationComplete( tr("Select Influences Vertices").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } void ViewWindow::jointSelectInfluencedPoints() { m_model->beginSelectionDifference(); Model::InfluenceList ilist; Model::InfluenceList::iterator iit; list< int > jointList; list< int >::iterator it; unsigned pcount = m_model->getPointCount(); m_model->getSelectedBoneJoints( jointList ); for ( it = jointList.begin(); it != jointList.end(); it++ ) { for ( unsigned p = 0; p < pcount; p++ ) { m_model->getPointInfluences( p, ilist ); for ( iit = ilist.begin(); iit != ilist.end(); iit++ ) { if ( (*iit).m_boneId == *it ) { m_model->selectPoint( p ); } } } } m_model->endSelectionDifference(); m_model->operationComplete( tr("Select Influenced Points").toUtf8() ); m_viewPanel->modelUpdatedEvent(); } mm3d-1.3.15/src/libmm3d/000077500000000000000000000000001466047437300145755ustar00rootroot00000000000000mm3d-1.3.15/src/libmm3d/Makefile.am000066400000000000000000000041711466047437300166340ustar00rootroot00000000000000noinst_LIBRARIES = libmm3d.a libmm3d_HFILES = \ binutil.h \ bsptree.h \ cal3dfilter.h \ cmdlinemgr.h \ cobfilter.h \ datadest.h \ filedatadest.h \ memdatadest.h \ d3dfilter.h \ datasource.h \ filedatasource.h \ memdatasource.h \ drawcontext.h \ dxffilter.h \ endianconfig.h \ file_closer.h \ filefactory.h \ filtermgr.h \ glheaders.h \ glmath.h \ iqefilter.h \ local_array.h \ local_ptr.h \ log.h \ lwofilter.h \ md2filter-anorms.h \ md2filter.h \ md3filter.h \ mesh.h \ misc.h \ mlocale.h \ mm3dconfig.h \ mm3dfilter.h \ mm3dfilter_ref.h \ mm3dport.h \ mm3dreg.h \ mm3dtypes.h \ model.h \ modelfilter.h \ modelstatus.h \ modelundo.h \ modelutil.h \ ms3dfilter.h \ msg.h \ objfilter.h \ pcxtex.h \ raii.h \ rawtex.h \ release_ptr.h \ smdfilter.h \ sorted_list.h \ texmgr.h \ texscale.h \ texture.h \ tgatex.h \ triprim.h \ translate.h \ txtfilter.h \ undo.h \ undomgr.h \ util.h \ weld.h libmm3d_a_SOURCES = \ bsptree.cc \ cal3dfilter.cc \ cmdlinemgr.cc \ cobfilter.cc \ d3dfilter.cc \ datadest.cc \ filedatadest.cc \ memdatadest.cc \ datasource.cc \ filedatasource.cc \ memdatasource.cc \ dxffilter.cc \ filefactory.cc \ filtermgr.cc \ glmath.cc \ iqefilter.cc \ log.cc \ lwofilter.cc \ mesh.cc \ misc.cc \ mlocale.cc \ model.cc model_anim.cc model_bool.cc model_copy.cc model_draw.cc model_group.cc model_influence.cc model_inner.cc model_insert.cc model_meta.cc model_ops.cc model_print.cc model_proj.cc model_select.cc model_texture.cc \ modelfilter.cc \ modelstatus.cc \ modelundo.cc \ modelutil.cc \ md2filter.cc \ md3filter.cc \ mm3dfilter.cc \ mm3dfilter_ref.cc \ mm3dreg.cc \ mm3dport.cc \ ms3dfilter.cc \ msg.cc \ objfilter.cc \ smdfilter.cc \ texmgr.cc \ texscale.cc \ texture.cc \ triprim.cc \ translate.cc \ undo.cc \ undomgr.cc \ weld.cc \ pcxtex.cc \ rawtex.cc \ tgatex.cc \ txtfilter.cc \ $(libmm3d_HFILES) AM_CPPFLAGS = $(CORE_PROFILE) $(COVFLAGS) -Wall -I$(srcdir)/.. -DMM3D_EDIT $(all_includes) $(GL_CFLAGS) wc: wc `ls *.h *.cpp *.cc *.y *.l 2> /dev/null | grep -v moc.cc | grep -v "\.base\." ` | sort -n CLEANFILES = *.gcno *.gcda mm3d-1.3.15/src/libmm3d/binutil.h000066400000000000000000000022371466047437300164200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BINUTIL_H #define __BINUTIL_H template bool bin_read( T & dest, uint8_t * & src, unsigned & len ) { if ( len >= sizeof( T ) ) { dest = * (T*) src; src += sizeof( T ); len -= sizeof( T ); return true; } else { return false; } } #endif // __BINUTIL_H mm3d-1.3.15/src/libmm3d/bsptree.cc000066400000000000000000000464571466047437300165700ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include #include #include #include "glheaders.h" #include "bsptree.h" #include "glmath.h" #include "log.h" #include "model.h" // Yes, it's hackish std::list< BspTree::Poly * > BspTree::Poly::s_recycle; std::list< BspTree::Node * > BspTree::Node::s_recycle; int BspTree::Poly::s_allocated = 0; int BspTree::Node::s_allocated = 0; void normalize( float * val ) { float mag = 0.0f; int t; for ( t = 0; t < 3; t++ ) { mag += val[t] * val[t]; } mag = sqrt( mag ); for ( t = 0; t < 3; t++ ) { val[t] = val[t] / mag; } } float dot_product( float * val1, float * val2 ) { return ( val1[0] * val2[0] ) + ( val1[1] * val2[1] ) + ( val1[2] * val2[2] ); } bool float_equiv( float rhs, float lhs ) { if ( fabs( rhs - lhs ) < 0.0001f ) { return true; } else { return false; } } static void _setMaterial( DrawingContext * context, int texture, Model::Material * material ) { float diffuse[4]; diffuse[0] = material->m_diffuse[0]; diffuse[1] = material->m_diffuse[1]; diffuse[2] = material->m_diffuse[2]; diffuse[3] = 1.0f; glMaterialfv( GL_FRONT, GL_AMBIENT, material->m_ambient ); glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse ); glMaterialfv( GL_FRONT, GL_SPECULAR, material->m_specular ); glMaterialfv( GL_FRONT, GL_EMISSION, material->m_emissive ); glMaterialf( GL_FRONT, GL_SHININESS, material->m_shininess ); context->m_currentTexture = texture; if ( material->m_type == Model::Material::MATTYPE_TEXTURE ) { glBindTexture( GL_TEXTURE_2D, context->m_matTextures[ context->m_currentTexture ] ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, material->m_sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, material->m_tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); } } int BspTree::Poly::s_nextId = 0; void BspTree::Poly::calculateNormal() { float A = coord[0][1] * (coord[1][2] - coord[2][2]) + coord[1][1] * (coord[2][2] - coord[0][2]) + coord[2][1] * (coord[0][2] - coord[1][2]); float B = coord[0][2] * (coord[1][0] - coord[2][0]) + coord[1][2] * (coord[2][0] - coord[0][0]) + coord[2][2] * (coord[0][0] - coord[1][0]); float C = coord[0][0] * (coord[1][1] - coord[2][1]) + coord[1][0] * (coord[2][1] - coord[0][1]) + coord[2][0] * (coord[0][1] - coord[1][1]); float len = sqrt((A * A) + (B * B) + (C * C)); norm[0] = A / len; norm[1] = B / len; norm[2] = C / len; calculateD(); //printf( "normal: %f %f %f\n", norm[0], norm[1], norm[2] ); //printf( "d: %f\n", d ); } void BspTree::Poly::calculateD() { d = dot_product( coord[0], norm ); } void BspTree::Poly::intersection( float * p1, float * p2, float * po, float & place ) { float interval = 0.25; float dtemp = 0.0f; place = 0.5f; bool greater = false; if ( dot_product( p2, norm ) > d ) { greater = true; } if ( dot_product( p1, norm ) > d ) { if ( greater ) { //printf( "no intersection!\n" ); return; } } else { if ( !greater ) { //printf( "no intersection!\n" ); return; } } float diff[3]; float oldPlace = place; diff[0] = p2[0] - p1[0]; diff[1] = p2[1] - p1[1]; diff[2] = p2[2] - p1[2]; do { po[0] = diff[0] * place + p1[0]; po[1] = diff[1] * place + p1[1]; po[2] = diff[2] * place + p1[2]; dtemp = dot_product( po, norm ); oldPlace = place; if ( greater ) { place += ( dtemp > d ) ? -interval : interval; } else { place += ( dtemp > d ) ? interval : -interval; } interval = interval * 0.5f; } while ( !float_equiv( d, dtemp ) ); place = oldPlace; } void BspTree::Poly::render( DrawingContext * context ) { //printf( "render triangle %d\n", id ); if ( context->m_currentTexture != texture ) { glEnd(); _setMaterial( context, texture, static_cast< Model::Material *>( material ) ); glBegin( GL_TRIANGLES ); } Model::Triangle * tri = static_cast< Model::Triangle *>( triangle ); if ( tri->m_visible ) { for ( int i = 0; i < 3; i++ ) { glTexCoord2f( s[ i ], t[ i ] ); glNormal3fv( drawNormals[i] ); glVertex3fv( coord[i] ); } } } void BspTree::Poly::print() { //printf( "Poly %d\n", id ); //printf( " coord[0] = %f %f %f\n", coord[0][0], coord[0][1], coord[0][2] ); //printf( " coord[1] = %f %f %f\n", coord[1][0], coord[1][1], coord[1][2] ); //printf( " coord[2] = %f %f %f\n", coord[2][0], coord[2][1], coord[2][2] ); //printf( " n = %f %f %f\n", norm[0], norm[1], norm[2] ); } void BspTree::addPoly( BspTree::Poly * p ) { Node * n = Node::get(); n->self = p; if ( m_root == NULL ) { m_root = n; } else { m_root->addChild( n ); } } void BspTree::render( float * point, DrawingContext * context ) { if ( m_root ) { _setMaterial( context, m_root->self->texture, static_cast< Model::Material *>( m_root->self->material ) ); glEnable( GL_TEXTURE_2D ); glBegin( GL_TRIANGLES ); m_root->render( point, context ); glEnd(); } } void BspTree::clear() { if ( m_root ) { m_root->release(); m_root = NULL; } } BspTree::Poly * BspTree::Poly::get() { if ( !s_recycle.empty() ) { Poly * n = s_recycle.front(); s_recycle.pop_front(); return n; } else { return new Poly; } } void BspTree::Poly::release() { s_recycle.push_front( this ); } BspTree::Node * BspTree::Node::get() { if ( !s_recycle.empty() ) { Node * n = s_recycle.front(); s_recycle.pop_front(); n->self = NULL; n->left = NULL; n->right = NULL; return n; } else { return new Node; } } void BspTree::Node::release() { if ( left ) { left->release(); } if ( self ) { self->release(); } if ( right ) { right->release(); } s_recycle.push_front( this ); } void BspTree::Node::splitNodes( int idx1, int idx2, int idx3, float * p1, float * p2, BspTree::Node * n1, BspTree::Node * n2, const float & place1, const float & place2 ) { n1->self = Poly::get(); //printf( "split node poly: %d\n", n1->self->id ); for ( int i = 0; i < 3; i++ ) { n1->self->coord[0][i] = p1[i]; n1->self->coord[1][i] = self->coord[idx2][i]; n1->self->coord[2][i] = self->coord[idx3][i]; n1->self->drawNormals[0][i] = self->norm[i]; n1->self->drawNormals[1][i] = self->drawNormals[idx2][i]; n1->self->drawNormals[2][i] = self->drawNormals[idx3][i]; } n1->self->s[0] = (self->s[ idx2 ] - self->s[ idx1 ]) * place1 + self->s[ idx1 ]; n1->self->s[1] = self->s[ idx2 ]; n1->self->s[2] = self->s[ idx3 ]; n1->self->t[0] = (self->t[ idx2 ] - self->t[ idx1 ]) * place1 + self->t[ idx1 ]; n1->self->t[1] = self->t[ idx2 ]; n1->self->t[2] = self->t[ idx3 ]; n1->self->calculateNormal(); n1->self->texture = self->texture; n1->self->triangle = self->triangle; n1->self->material = self->material; n2->self = Poly::get(); //printf( "split node poly: %d\n", n2->self->id ); for ( int i = 0; i < 3; i++ ) { n2->self->coord[0][i] = p1[i]; n2->self->coord[1][i] = self->coord[idx3][i]; n2->self->coord[2][i] = p2[i]; n2->self->drawNormals[0][i] = self->norm[i]; n2->self->drawNormals[1][i] = self->drawNormals[idx3][i]; n2->self->drawNormals[2][i] = self->norm[i]; } n2->self->s[0] = (self->s[ idx2 ] - self->s[ idx1 ]) * place1 + self->s[ idx1 ]; n2->self->s[1] = self->s[ idx3 ]; n2->self->s[2] = (self->s[ idx3 ] - self->s[ idx1 ]) * place2 + self->s[ idx1 ]; n2->self->t[0] = (self->t[ idx2 ] - self->t[ idx1 ]) * place1 + self->t[ idx1 ]; n2->self->t[1] = self->t[ idx3 ]; n2->self->t[2] = (self->t[ idx3 ] - self->t[ idx1 ]) * place2 + self->t[ idx1 ]; n2->self->calculateNormal(); n2->self->texture = self->texture; n2->self->triangle = self->triangle; n2->self->material = self->material; for ( int i = 0; i < 3; i++ ) { self->coord[idx2][i] = p1[i]; self->coord[idx3][i] = p2[i]; self->drawNormals[idx2][i] = self->norm[i]; self->drawNormals[idx3][i] = self->norm[i]; } self->s[idx2] = (self->s[ idx2 ] - self->s[ idx1 ]) * place1 + self->s[ idx1 ]; self->s[idx3] = (self->s[ idx3 ] - self->s[ idx1 ]) * place2 + self->s[ idx1 ]; self->t[idx2] = (self->t[ idx2 ] - self->t[ idx1 ]) * place1 + self->t[ idx1 ]; self->t[idx3] = (self->t[ idx3 ] - self->t[ idx1 ]) * place2 + self->t[ idx1 ]; self->calculateD(); } void BspTree::Node::splitNode( int idx1, int idx2, int idx3, float * p1, BspTree::Node * n1, const float & place ) { n1->self = Poly::get(); //printf( "split node poly: %d\n", n1->self->id ); for ( int i = 0; i < 3; i++ ) { n1->self->coord[0][i] = self->coord[idx1][i]; n1->self->coord[1][i] = self->coord[idx2][i]; n1->self->coord[2][i] = p1[i]; n1->self->drawNormals[0][i] = self->drawNormals[idx1][i]; n1->self->drawNormals[1][i] = self->drawNormals[idx2][i]; n1->self->drawNormals[2][i] = self->norm[i]; } n1->self->s[0] = self->s[ idx1 ]; n1->self->s[1] = self->s[ idx2 ]; n1->self->s[2] = (self->s[ idx3 ] - self->s[ idx2 ]) * place + self->s[ idx2 ]; n1->self->t[0] = self->t[ idx1 ]; n1->self->t[1] = self->t[ idx2 ]; n1->self->t[2] = (self->t[ idx3 ] - self->t[ idx2 ]) * place + self->t[ idx2 ]; n1->self->calculateNormal(); n1->self->texture = self->texture; n1->self->triangle = self->triangle; n1->self->material = self->material; for ( int i = 0; i < 3; i++ ) { self->coord[idx2][i] = p1[i]; self->drawNormals[idx2][i] = self->norm[i]; } self->s[idx2] = (self->s[ idx3 ] - self->s[ idx2 ]) * place + self->s[ idx2 ]; self->t[idx2] = (self->t[ idx3 ] - self->t[ idx2 ]) * place + self->t[ idx2 ]; self->calculateD(); } void BspTree::Node::addChild( Node * n ) { float d1 = dot_product( self->norm, n->self->coord[0] ); float d2 = dot_product( self->norm, n->self->coord[1] ); float d3 = dot_product( self->norm, n->self->coord[2] ); int i1 = 0; int i2 = 0; int i3 = 0; if ( !float_equiv( d1, self->d ) ) i1 = ( d1 < self->d ) ? -1 : 1; if ( !float_equiv( d2, self->d ) ) i2 = ( d2 < self->d ) ? -1 : 1; if ( !float_equiv( d3, self->d ) ) i3 = ( d3 < self->d ) ? -1 : 1; //printf( "self d = %f\n", self->d ); //printf( "addChild d = %f %f %f\n", d1, d2, d3 ); //printf( "addChild i = %d %d %d\n", i1, i2, i3 ); // This will catch co-plane also... which should be fine if ( i1 <= 0 && i2 <= 0 && i3 <= 0 ) { //printf( "add right\n" ); if ( left ) { left->addChild( n ); } else { left = n; } return; } if ( i1 >= 0 && i2 >= 0 && i3 >= 0 ) { //printf( "add left\n" ); if ( right ) { right->addChild( n ); } else { right = n; } return; } //printf( "split\n" ); float p1[3]; float p2[3]; float place1 = 0.0f; float place2 = 0.0f; if ( i1 == 0 || i2 == 0 || i3 == 0 ) { // one of the vertices is on the plane //printf( "split on vertex\n" ); Node * n1 = Node::get(); if ( i1 == 0 ) { self->intersection( n->self->coord[1], n->self->coord[2], p1, place1 ); n->splitNode( 0, 1, 2, p1, n1, place1 ); if ( i2 < 0 ) { if ( right ) addChild( n ); else right = n; if ( left ) left->addChild( n1 ); else left = n1; } else { if ( right ) addChild( n1 ); else right = n1; if ( left ) left->addChild( n ); else left = n; } } else if ( i2 == 0 ) { self->intersection( n->self->coord[2], n->self->coord[0], p1, place1 ); n->splitNode( 1, 2, 0, p1, n1, place1 ); if ( i1 < 0 ) { if ( right ) addChild( n1 ); else right = n1; if ( left ) left->addChild( n ); else left = n; } else { if ( right ) addChild( n ); else right = n; if ( left ) left->addChild( n1 ); else left = n1; } } else if ( i3 == 0 ) { self->intersection( n->self->coord[0], n->self->coord[1], p1, place1 ); n->splitNode( 2, 0, 1, p1, n1, place1 ); if ( i1 < 0 ) { if ( right ) addChild( n ); else right = n; if ( left ) left->addChild( n1 ); else left = n1; } else { if ( right ) addChild( n1 ); else right = n1; if ( left ) left->addChild( n ); else left = n; } } } else { Node * n1 = Node::get(); Node * n2 = Node::get(); if ( i1 == i2 ) { //printf( "split 1/2\n" ); self->intersection( n->self->coord[2], n->self->coord[0], p1, place1 ); self->intersection( n->self->coord[2], n->self->coord[1], p2, place2 ); //printf( "split at %f %f %f\n", p1[0], p1[1], p1[2] ); //printf( "split at %f %f %f\n", p2[0], p2[1], p2[2] ); n->splitNodes( 2, 0, 1, p1, p2, n1, n2, place1, place2 ); if ( i3 < 0 ) { n1->left = n2; if ( right ) right->addChild( n1 ); else right = n1; if ( left ) left->addChild( n ); else left = n; } else { n1->right = n2; if ( left ) left->addChild( n1 ); else left = n1; if ( right ) right->addChild( n ); else right = n; } } else if ( i1 == i3 ) { //printf( "split 1/3\n" ); self->intersection( n->self->coord[1], n->self->coord[2], p1, place1 ); self->intersection( n->self->coord[1], n->self->coord[0], p2, place2 ); //printf( "split at %f %f %f\n", p1[0], p1[1], p1[2] ); //printf( "split at %f %f %f\n", p2[0], p2[1], p2[2] ); n->splitNodes( 1, 2, 0, p1, p2, n1, n2, place1, place2 ); if ( i2 < 0 ) { n1->left = n2; if ( right ) right->addChild( n1 ); else right = n1; if ( left ) left->addChild( n ); else left = n; } else { n1->right = n2; if ( left ) left->addChild( n1 ); else left = n1; if ( right ) right->addChild( n ); else right = n; } } else if ( i2 == i3 ) { self->intersection( n->self->coord[0], n->self->coord[1], p1, place1 ); self->intersection( n->self->coord[0], n->self->coord[2], p2, place2 ); n->splitNodes( 0, 1, 2, p1, p2, n1, n2, place1, place2 ); if ( i1 < 0 ) { n1->left = n2; if ( right ) right->addChild( n1 ); else right = n1; if ( left ) left->addChild( n ); else left = n; } else { n1->right = n2; if ( left ) left->addChild( n1 ); else left = n1; if ( right ) right->addChild( n ); else right = n; } } } } void BspTree::Node::render( float * point, DrawingContext * context ) { float d = dot_product( self->norm, point ); if ( d < self->d ) { if ( right ) { right->render( point, context ); } self->render( context ); if ( left ) { left->render( point, context ); } } else { if ( left ) { left->render( point, context ); } self->render( context ); if ( right ) { right->render( point, context ); } } } void BspTree::Poly::stats() { log_debug( "BspTree::Poly: %" PORTuSIZE "/%d\n", s_recycle.size(), s_allocated ); } void BspTree::Node::stats() { log_debug( "BspTree::Node: %" PORTuSIZE "/%d\n", s_recycle.size(), s_allocated ); } int BspTree::Poly::flush() { int c = 0; std::list::iterator it = s_recycle.begin(); while ( it != s_recycle.end() ) { delete *it; it++; c++; } s_recycle.clear(); return c; } int BspTree::Node::flush() { int c = 0; std::list::iterator it = s_recycle.begin(); while ( it != s_recycle.end() ) { delete *it; it++; c++; } s_recycle.clear(); return c; } #if 0 int main( int argc, char * argv[] ) { char input[128]; BspTree tree; while ( fgets( input, sizeof(input), stdin ) ) { float coord[0][3]; float coord[1][3]; float coord[2][3]; if ( input[0] == 'c' ) { printf( "camera\n" ); if ( sscanf(&input[1], "%f %f %f", &coord[0][0], &coord[0][1], &coord[0][2] ) == 3 ) { printf( "got camera point\n" ); tree.render( coord[0] ); } } else if ( sscanf( input, "%f %f %f %f %f %f %f %f %f", &coord[0][0], &coord[0][1], &coord[0][2], &coord[1][0], &coord[1][1], &coord[1][2], &coord[2][0], &coord[2][1], &coord[2][2] ) == 9 ) { Poly * p = Poly::get(); printf( "read poly: %d\n", p->id ); for ( int i = 0; i < 3; i++ ) { p->coord[0][i] = coord[0][i]; p->coord[1][i] = coord[1][i]; p->coord[2][i] = coord[2][i]; } p->calculateNormal(); tree.addPoly( p ); } } return 0; } #endif // 0 mm3d-1.3.15/src/libmm3d/bsptree.h000066400000000000000000000062271466047437300164210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BSPTREE_H #define __BSPTREE_H #include "drawcontext.h" #include class BspTree { public: class Poly { public: static Poly * get(); void release(); static int flush(); static void stats(); int id; float coord[3][3]; float drawNormals[3][3]; int texture; void * material; // Yeah, yeah, I know... it's hackish void * triangle; // Yeah, yeah, I know... it's hackish float s[3]; // texture coordinates float t[3]; float norm[3]; float d; // dot product void calculateNormal(); void calculateD(); void intersection( float * p1, float * p2, float * po, float & place ); void render( DrawingContext * context ); void print(); protected: Poly() : id( ++s_nextId ) { s_allocated++; }; virtual ~Poly() { s_allocated--; }; static int s_nextId; static std::list< Poly * > s_recycle; static int s_allocated; }; class Node { public: static Node * get(); void release(); static int flush(); static void stats(); void addChild( Node * n ); void render( float * point, DrawingContext * context ); void splitNodes( int idx1, int idx2, int idx3, float * p1, float * p2, Node * n1, Node * n2, const float & place1, const float & place2 ); void splitNode( int idx1, int idx2, int idx3, float * p1, Node * n1, const float & place ); Poly * self; Node * left; Node * right; protected: Node() : self( NULL ), left( NULL ), right( NULL ) { s_allocated++;}; virtual ~Node() { s_allocated--; }; static std::list< Node * > s_recycle; static int s_allocated; }; BspTree() : m_root( NULL ) {}; virtual ~BspTree() { clear(); }; void addPoly( Poly * p ); void render( float * point, DrawingContext * context ); void clear(); protected: Node * m_root; }; #endif // __BSPTREE_H mm3d-1.3.15/src/libmm3d/cal3dfilter.cc000066400000000000000000002364531466047437300173150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // Cal 3D notes: // // Main file may be .cal or .cfg // // The .cal file may be ascii text .ini style, or XML. The XML .cal file // format is not supported. // // Versions: // // Version numbers are decimal // // Binary version 700 = XML Version 900 or 1000 // // XML version 900: // File has a
element with version // XML version 1000: // No header, version is part of top-level tag // // Binary version 1200: // Animation has an extra flag field at the start that indicates if // the animation tracks are compressed, plus possibly compressed tracks. // FIXME things to test: // // Read compressed tracks (TEST) // Are .cal and .cfg different? Looks like mesh/animation keys // don't have names (mesh= instead of mesh_foo=) #include "cal3dfilter.h" #include "model.h" #include "texture.h" #include "log.h" #include "endianconfig.h" #include "misc.h" #include "filtermgr.h" #include "texmgr.h" #include "mesh.h" #include "modelstatus.h" #include "mm3dport.h" #include "translate.h" #include "datadest.h" #include "datasource.h" #include "file_closer.h" #include "local_array.h" #include "release_ptr.h" #include #include #include #include #include #include #include using std::list; using std::string; #ifdef PLUGIN static Cal3dFilter * s_filter = NULL; #endif // PLUGIN // These values are decimal, not hexadecimal #define CAL3D_MIN_BVERSION 700 #define CAL3D_MAX_BVERSION 1200 #define CAL3D_MIN_XVERSION 900 #define CAL3D_MAX_XVERSION 1000 // Versions where formats changed. Files with versions equal to or later than // these may make use of newer features. #define CAL3D_COMP_ANIM_VERSION 1200 // Compressed animation tracks #define CAL3D_NO_XHEADER_VERSION 1000 // XML files without
tags (version goes in top-level tag) // File magic number values #define CAL3D_MAGIC_SIZE 4 #define CAL3D_MAGIC_MATERIAL "CRF\0" #define CAL3D_MAGIC_MESH "CMF\0" #define CAL3D_MAGIC_SKELETON "CSF\0" #define CAL3D_MAGIC_ANIMATION "CAF\0" const Model::AnimationModeE MODE = Model::ANIMMODE_SKELETAL; static char * _skipSpace( char * str ) { while ( isspace( str[0] ) ) { str++; } return str; } static bool isallowed( char ch ) { switch ( ch ) { case '_': case '-': case '.': case '+': return true; default: break; } return false; } static void _escapeCal3dName( std::string & name ) { size_t i = 0; while ( i < name.size() ) { if ( isspace( name[i] ) ) { name[i] = '_'; i++; } else if ( !isalnum( name[i] ) && !isallowed( name[i] ) ) { name.erase(i,1); } else { i++; } } } static void _escapeFileName( std::string & file ) { _escapeCal3dName( file ); // for now this is the same code } static void _strtolower( std::string & str ) { size_t i = 0; for ( i = 0; i < str.size(); i++ ) { str[i] = tolower( str[i] ); } } Cal3dFilter::Cal3dOptions::Cal3dOptions() : m_singleMeshFile( true ), m_xmlMatFile( true ) { } Cal3dFilter::Cal3dOptions::~Cal3dOptions() { } void Cal3dFilter::Cal3dOptions::setOptionsFromModel( Model * m ) { char value[32]; if ( m->getMetaData( "cal3d_single_mesh_file", value, sizeof(value) ) ) { // Non-zero, single mesh m_singleMeshFile = atoi(value) != 0; } if ( m->getMetaData( "cal3d_xml_material", value, sizeof(value) ) ) { // Non-zero, use XML format for material m_xmlMatFile = atoi(value) != 0; } } Cal3dFilter::Cal3dFilter() : m_model( NULL ), m_options( NULL ) { m_formats.push_back( "cal" ); m_formats.push_back( "cfg" ); } Cal3dFilter::~Cal3dFilter() { } Model::ModelErrorE Cal3dFilter::readFile( Model * model, const char * const filename ) { Model::ModelErrorE err = Model::ERROR_NONE; // Use these to determine what versions of files to write. m_maxBinaryVersion = CAL3D_MIN_BVERSION; m_maxXrfVersion = CAL3D_MIN_XVERSION; if ( (err = readFileToBuffer( filename, m_fileBuf, m_fileLength )) == Model::ERROR_NONE ) { local_array releaseBuf( m_fileBuf ); m_bufPos = m_fileBuf; m_modelPath = ""; m_modelBaseName = ""; m_modelFullName = ""; normalizePath( filename, m_modelFullName, m_modelPath, m_modelBaseName ); m_currentPath = m_modelPath; model->setFilename( m_modelFullName.c_str() ); m_model = model; // Read specified file based on magic if ( memcmp( m_fileBuf, CAL3D_MAGIC_SKELETON, CAL3D_MAGIC_SIZE ) == 0 ) { err = readSkeletonFile( m_fileBuf, m_fileLength ); } else if ( memcmp( m_fileBuf, CAL3D_MAGIC_ANIMATION, CAL3D_MAGIC_SIZE ) == 0 ) { err = readAnimationFile( m_fileBuf, m_fileLength ); } else if ( memcmp( m_fileBuf, CAL3D_MAGIC_MESH, CAL3D_MAGIC_SIZE ) == 0 ) { m_model->updateMetaData( "cal3d_single_mesh_file", "1" ); err = readMeshFile( m_fileBuf, m_fileLength ); } else if ( memcmp( m_fileBuf, CAL3D_MAGIC_MATERIAL, CAL3D_MAGIC_SIZE ) == 0 ) { err = readMaterialFile( m_fileBuf, m_fileLength ); } else if ( m_fileBuf[0] == '<' ) { // probably an XML file, we only support material files err = readXSubFile( m_fileBuf, m_fileLength ); } else { err = readCal3dFile( m_fileBuf, m_fileLength ); } m_model->setupJoints(); log_debug( "Cal3D Model:\n" ); log_debug( " vertices: %d\n", m_model->getVertexCount() ); log_debug( " faces: %d\n", m_model->getTriangleCount() ); log_debug( " groups: %d\n", m_model->getGroupCount() ); log_debug( " bones: %d\n", m_model->getBoneJointCount() ); log_debug( " materials: %d\n", m_model->getTextureCount() ); m_fileBuf = NULL; m_bufPos = NULL; } char version[32]; PORT_snprintf( version, sizeof(version), "%d", m_maxXrfVersion ); model->updateMetaData( "cal3d_xrf_version", version ); PORT_snprintf( version, sizeof(version), "%d", m_maxBinaryVersion ); model->updateMetaData( "cal3d_binary_version", version ); return err; } Model::ModelErrorE Cal3dFilter::writeFile( Model * model, const char * const filename, ModelFilter::Options * o ) { if ( filename == NULL || filename[0] == '\0' ) { return Model::ERROR_BAD_ARGUMENT; } m_modelPath = ""; m_modelBaseName = ""; m_modelFullName = ""; normalizePath( filename, m_modelFullName, m_modelPath, m_modelBaseName ); m_model = model; m_options = NULL; const char * ext = strrchr( filename, '.' ); if ( ext ) { ext++; if ( strcasecmp( ext, "CSF" ) == 0 ) { return writeSkeletonFile( filename, model ); } else if ( strcasecmp( ext, "CAF" ) == 0 ) { if ( model->getAnimCount( MODE ) > 0 ) { return writeAnimationFile( filename, model, 0 ); } return Model::ERROR_BAD_DATA; } else if ( strcasecmp( ext, "CMF" ) == 0 ) { return writeMeshFile( filename, model ); } else if ( strcasecmp( ext, "CRF" ) == 0 ) { if ( model->getTextureCount() > 0 ) { return writeMaterialFile( filename, model, 0 ); } return Model::ERROR_BAD_DATA; } else if ( strcasecmp( ext, "XRF" ) == 0 ) { if ( model->getTextureCount() > 0 ) { return writeXMaterialFile( filename, model, 0 ); } return Model::ERROR_BAD_DATA; } else if ( toupper(ext[0]) == 'X' ) { // Assume XML file return Model::ERROR_UNSUPPORTED_VERSION; } else if ( strcasecmp( ext, "cfg" ) == 0 ) { return writeCfgFile( filename, model, o ); } // Assume Cal3D master file return writeCal3dFile( filename, model, o ); } else { return writeCal3dFile( filename, model, o ); } } bool Cal3dFilter::canRead( const char * filename ) { log_debug( "canRead( %s )\n", filename ); log_debug( " true\n" ); return true; } bool Cal3dFilter::canWrite( const char * filename ) { log_debug( "canWrite( %s )\n", filename ); log_debug( " false\n" ); return false; } bool Cal3dFilter::canExport( const char * filename ) { log_debug( "canExport( %s )\n", filename ); log_debug( " true\n" ); return true; } bool Cal3dFilter::isSupported( const char * filename ) { log_debug( "isSupported( %s )\n", filename ); unsigned len = strlen(filename); for ( std::list::const_iterator it = m_formats.begin(); it != m_formats.end(); ++it ) { const std::string fmt = std::string(".") + *it; unsigned fmtlen = fmt.size(); if ( len >= fmtlen && strcasecmp( &filename[len-fmtlen], fmt.c_str() ) == 0 ) { log_debug( " true\n" ); return true; } } log_debug( " false\n" ); return false; } list< string > Cal3dFilter::getReadTypes() { list rval; for ( std::list::const_iterator it = m_formats.begin(); it != m_formats.end(); ++it ) { rval.push_back( std::string("*.") + *it ); } return rval; } list< string > Cal3dFilter::getWriteTypes() { list rval; for ( std::list::const_iterator it = m_formats.begin(); it != m_formats.end(); ++it ) { rval.push_back( std::string("*.") + *it ); } return rval; } //------------------------------------------------------------------ // Protected Methods //------------------------------------------------------------------ bool Cal3dFilter::listHas( const std::list & l, const std::string & val ) { std::list::const_iterator it = l.begin(); while ( it != l.end() ) { if ( (*it) == val ) { return true; } it++; } return false; } std::string Cal3dFilter::addExtension( const std::string file, const std::string ext ) { if ( file.size() > ext.size() ) { std::string cmp = std::string(".") + ext; size_t len = cmp.size() - file.size(); if ( strcasecmp( &(file.c_str()[file.size() - len]), ext.c_str() ) == 0 ) { return file; } } return file + std::string(".") + ext; } bool Cal3dFilter::versionIsValid( FileTypeE type, int version ) { if ( version >= CAL3D_MIN_BVERSION && version <= CAL3D_MAX_BVERSION ) { return true; } return false; } bool Cal3dFilter::xversionIsValid( FileTypeE type, int version ) { if ( version >= CAL3D_MIN_XVERSION && version <= CAL3D_MAX_XVERSION ) { return true; } return false; } //------------------------------------------------------------------ // Common read functions Model::ModelErrorE Cal3dFilter::readSubFile( const char * filename ) { Model::ModelErrorE err = Model::ERROR_NONE; std::string oldPath = m_currentPath; std::string fullPath = m_currentPath + std::string( "/" ) + filename; uint8_t * buf; size_t len; if ( (err = readFileToBuffer( fullPath.c_str(), buf, len )) == Model::ERROR_NONE ) { local_array releaseBuf(buf); std::string baseName; std::string fullName; normalizePath( fullPath.c_str(), fullName, m_currentPath, baseName ); const char * part = strrchr( filename, '/' ); if ( part ) { part++; } else { part = filename; } const char * ext = strrchr( filename, '.' ); if ( ext ) { m_modelPartName.assign( part, ext - part ); m_modelPartExt = &ext[1]; } else { m_modelPartName = part; } // Read specified file based on magic if ( memcmp( buf, CAL3D_MAGIC_SKELETON, CAL3D_MAGIC_SIZE ) == 0 ) { err = readSkeletonFile( buf, len ); } else if ( memcmp( buf, CAL3D_MAGIC_ANIMATION, CAL3D_MAGIC_SIZE ) == 0 ) { err = readAnimationFile( buf, len ); } else if ( memcmp( buf, CAL3D_MAGIC_MESH, CAL3D_MAGIC_SIZE ) == 0 ) { err = readMeshFile( buf, len ); } else if ( memcmp( buf, CAL3D_MAGIC_MATERIAL, CAL3D_MAGIC_SIZE ) == 0 ) { err = readMaterialFile( buf, len ); } else if ( buf[0] == '<' ) { // probably an XML file, try to parse it err = readXSubFile( buf, len ); } else { err = readCal3dFile( buf, len ); } m_currentPath = oldPath; } if ( err != Model::ERROR_NONE ) { std::string errStr = filename; errStr += ": "; errStr += Model::errorToString( err ); model_status( m_model, StatusError, STATUSTIME_LONG, "%s", errStr.c_str() ); } return err; } Model::ModelErrorE Cal3dFilter::readXSubFile( uint8_t * buf, size_t len ) { Model::ModelErrorE err = Model::ERROR_UNSUPPORTED_VERSION; uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; // Only support material XML files // Some versions of CAL3D files have a header element, some do not // If we see a HEADER element, get the MAGIC attribute if ( findXElement( "HEADER" ) ) { log_debug( "XML file has a
element\n" ); std::string magic = readXAttribute( "MAGIC" ); if ( strcmp( magic.c_str(), "XRF" ) == 0 ) { log_debug( "XML file is a material file\n" ); err = readXMaterialFile( buf, len ); } else { log_warning( "XML file is an unknown type: %s\n", magic.c_str() ); } } else if ( findXElement( "MATERIAL" ) ) { log_debug( "XML file does not have a header\n" ); log_debug( "XML file is a material file\n" ); err = readXMaterialFile( buf, len ); } else { log_debug( "Could not determine XML file type\n" ); log_warning( "XML file is an unsupported type (unrecognized root tag)\n" ); } m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return err; } Model::ModelErrorE Cal3dFilter::readCal3dFile( uint8_t * buf, size_t len ) { log_debug( "reading cal3d config file\n" ); Model::ModelErrorE rval = Model::ERROR_NONE; uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; string line = ""; string subfile = ""; // We start in the [model] section. Technically this is // incorrect, but it's an attempt to get around possible // bad files or bad parsing logic. This shouldn't hurt anything. // If there is a non-model section before the model section // it will be skipped. bool modelSection = true; // Save mesh, material, and animation files so we can // load them in our own preferred order. The only thing // we load as soon as we see it is the skeleton file. std::list< std::string > meshFiles; std::list< std::string > matFiles; std::list< std::string > animFiles; // If no usable data is found, the bracket count (ie, number of lines // that start with '<') is used to determine if the file was in XML format // so that we can give a more useful error message to the user. int bracketCount = 0; bool noData = true; while ( readBLine( line, len - (m_bufPos - m_fileBuf) ) ) { if ( modelSection ) { const char * str = _skipSpace( (char *) line.c_str() ); // only parse lines that are not empty or comments if ( str[0] && str[0] != '#' ) { if ( strncasecmp( str, "skeleton", 8 ) == 0 ) { noData = false; subfile = readLineFile( str ); if ( !subfile.empty() ) { log_debug( "loading skeleton file %s\n", subfile.c_str() ); readSubFile( subfile.c_str() ); } } else if ( strncasecmp( str, "mesh", 4 ) == 0 ) { noData = false; subfile = readLineFile( str ); if ( !subfile.empty() ) { meshFiles.push_back( subfile ); } } else if ( strncasecmp( str, "animation", 9 ) == 0 ) { noData = false; std::string animLabel = readLineKey( str ); subfile = readLineFile( str ); if ( !subfile.empty() && !animLabel.empty() ) { m_model->updateMetaData( animLabel.c_str(), subfile.c_str() ); if ( !listHas( animFiles, subfile ) ) { animFiles.push_back( subfile ); } } } else if ( strncasecmp( str, "material", 8 ) == 0 ) { noData = false; subfile = readLineFile( str ); if ( !subfile.empty() ) { matFiles.push_back( subfile ); } } else if ( strncasecmp( str, "path", 4 ) == 0 ) { noData = false; string value = readLineFile( str ); m_model->updateMetaData( "cal3d_path", value.c_str() ); } else if ( strncasecmp( str, "scale", 5 ) == 0 ) { noData = false; string value = readLineFile( str ); m_model->updateMetaData( "cal3d_scale", value.c_str() ); } else if ( strncasecmp( str, "rotate", 6 ) == 0 ) { noData = false; string value = readLineFile( str ); m_model->updateMetaData( "cal3d_rotate", value.c_str() ); } else if ( str[0] == '[' ) { // looks like a new section, see if it's a model section str++; while ( isspace( str[0] ) ) { str++; } if ( strncasecmp( str, "model", 5 ) == 0 ) { modelSection = true; } else { // Not a model section, skip it log_debug( "skipping [%s section\n", str ); modelSection = false; } } else if ( str[0] == '<' ) { ++bracketCount; } } if ( (m_bufPos - m_fileBuf) >= (int) m_fileLength ) { break; } } else { // Not in the model section, the only parsing we want // to do is to find the model section... const char * str = _skipSpace( (char *) line.c_str() ); if ( str[0] == '[' ) { // looks like a new section, see if it's a model section str++; while ( isspace( str[0] ) ) { str++; } if ( strncasecmp( str, "model", 5 ) == 0 ) { // Yes, it's a model section, enable the parsing logic log_debug( "entering model section\n" ); modelSection = true; } else { log_debug( "skipping [%s section\n", str ); modelSection = false; } } if ( (m_bufPos - m_fileBuf) >= (int) m_fileLength ) { break; } } } std::list< std::string >::iterator it; // If we didn't find any usable data, return an error. if ( noData ) { if ( bracketCount > 2 ) m_model->setFilterSpecificError( transll( QT_TRANSLATE_NOOP( "LowLevel", "MM3D does not support CAL3D files in XML format" ) ).c_str() ); else m_model->setFilterSpecificError( transll( QT_TRANSLATE_NOOP( "LowLevel", "The file does not contain any mesh or animation data" ) ).c_str() ); rval = Model::ERROR_FILTER_SPECIFIC; goto bail_out; } // Load materials first because meshes will reference them for ( it = matFiles.begin(); it != matFiles.end(); it++ ) { log_debug( "loading material file %s\n", (*it).c_str() ); readSubFile( (*it).c_str() ); } // Now load meshes for ( it = meshFiles.begin(); it != meshFiles.end(); it++ ) { log_debug( "loading mesh file %s\n", (*it).c_str() ); readSubFile( (*it).c_str() ); } m_model->updateMetaData( "cal3d_single_mesh_file", ( meshFiles.size() == 1 ) ? "1" : "0" ); // Now load animations for ( it = animFiles.begin(); it != animFiles.end(); it++ ) { log_debug( "loading animation file %s\n", (*it).c_str() ); readSubFile( (*it).c_str() ); } bail_out:; m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return rval; } Model::ModelErrorE Cal3dFilter::readSkeletonFile( uint8_t * buf, size_t len ) { uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; Model::ModelErrorE rval = Model::ERROR_NONE; if ( memcmp( CAL3D_MAGIC_SKELETON, m_bufPos, CAL3D_MAGIC_SIZE ) == 0 ) { m_bufPos += CAL3D_MAGIC_SIZE; int fileVersion = readBInt32(); int numBones = readBInt32(); if ( fileVersion > m_maxBinaryVersion ) m_maxBinaryVersion = fileVersion; log_debug( "skel version: %d (%x)\n", fileVersion, fileVersion ); bool success = true; if ( versionIsValid( FT_Skeleton, fileVersion ) ) { for ( int b = 0; success && b < numBones; b++ ) { success = readBBone(); } } else { rval = Model::ERROR_UNSUPPORTED_VERSION; log_error( "Unsupported CAL3D skeleton version %d\n", fileVersion ); } } else { rval = Model::ERROR_BAD_MAGIC; log_error( "Bad magic in skeleton file\n" ); } m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return rval; } Model::ModelErrorE Cal3dFilter::readMeshFile( uint8_t * buf, size_t len ) { uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; Model::ModelErrorE rval = Model::ERROR_NONE; if ( memcmp( CAL3D_MAGIC_MESH, m_bufPos, CAL3D_MAGIC_SIZE ) == 0 ) { m_bufPos += CAL3D_MAGIC_SIZE; int fileVersion = readBInt32(); int numSubMeshes = readBInt32(); if ( fileVersion > m_maxBinaryVersion ) m_maxBinaryVersion = fileVersion; log_debug( "mesh version: %d (%x)\n", fileVersion, fileVersion ); bool success = true; if ( versionIsValid( FT_Mesh, fileVersion ) ) { for ( int m = 0; success && m < numSubMeshes; m++ ) { success = readBSubMesh(); } } else { rval = Model::ERROR_UNSUPPORTED_VERSION; log_error( "Unsupported CAL3D mesh version %d\n", fileVersion ); } } else { rval = Model::ERROR_BAD_MAGIC; log_error( "Bad magic in mesh file\n" ); } m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return rval; } Model::ModelErrorE Cal3dFilter::readMaterialFile( uint8_t * buf, size_t len ) { uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; m_model->updateMetaData( "cal3d_xml_material", "0" ); Model::ModelErrorE rval = Model::ERROR_NONE; // We're going to add a material whether successful or not. Materials are // referenced by index so the material needs to exist even if it's not valid. Model::Material * mat = Model::Material::get(); mat->m_type = Model::Material::MATTYPE_BLANK; // assume mat->m_name = m_modelPartName; // this should be a sensible name mat->m_filename = ""; // none by default if ( memcmp( CAL3D_MAGIC_MATERIAL, m_bufPos, CAL3D_MAGIC_SIZE ) == 0 ) { m_bufPos += CAL3D_MAGIC_SIZE; int fileVersion = readBInt32(); if ( fileVersion > m_maxBinaryVersion ) m_maxBinaryVersion = fileVersion; log_debug( "mat version: %d (%x)\n", fileVersion, fileVersion ); if ( versionIsValid( FT_Material, fileVersion ) ) { Vector ambient; ambient[0] = ((float) readBUInt8()) / 255.0f; ambient[1] = ((float) readBUInt8()) / 255.0f; ambient[2] = ((float) readBUInt8()) / 255.0f; ambient[3] = ((float) readBUInt8()) / 255.0f; Vector diffuse; diffuse[0] = ((float) readBUInt8()) / 255.0f; diffuse[1] = ((float) readBUInt8()) / 255.0f; diffuse[2] = ((float) readBUInt8()) / 255.0f; diffuse[3] = ((float) readBUInt8()) / 255.0f; Vector specular; specular[0] = ((float) readBUInt8()) / 255.0f; specular[1] = ((float) readBUInt8()) / 255.0f; specular[2] = ((float) readBUInt8()) / 255.0f; specular[3] = ((float) readBUInt8()) / 255.0f; float shiny = readBFloat(); int numMaps = readBInt32(); if ( numMaps > 0 ) { // Texture-map, change type and add filename string filename; readBString( filename ); mat->m_type = Model::Material::MATTYPE_TEXTURE; mat->m_filename = normalizePath(filename.c_str(), m_currentPath.c_str()); // ignore everything else in the file } log_debug( "Reading material %d\n", m_model->getTextureCount() ); log_debug( " name %s\n", mat->m_name.c_str() ); log_debug( " num maps %d\n", numMaps ); log_debug( " file %s\n", mat->m_filename.c_str() ); log_debug( " diffuse %f,%f,%f\n", diffuse[0], diffuse[1], diffuse[2] ); log_debug( " ambient %f,%f,%f\n", ambient[0], ambient[1], ambient[2] ); log_debug( " specular %f,%f,%f\n", specular[0], specular[1], specular[2] ); log_debug( " shininess %f\n", shiny ); if ( numMaps > 1 ) { log_warning( " ignoring %d maps for %s\n", numMaps - 1, mat->m_name.c_str() ); } for ( int t = 0; t < 4; t++ ) { mat->m_diffuse[t] = diffuse[t]; mat->m_ambient[t] = ambient[t]; mat->m_specular[t] = specular[t]; mat->m_emissive[t] = 0.0f; } mat->m_shininess = shiny; } else { rval = Model::ERROR_UNSUPPORTED_VERSION; log_error( "Unsupported CAL3D material version %d\n", fileVersion ); } } else { rval = Model::ERROR_BAD_MAGIC; log_error( "Bad magic in material file\n" ); } getMaterialList( m_model ).push_back( mat ); m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return rval; } Model::ModelErrorE Cal3dFilter::readXMaterialFile( uint8_t * buf, size_t len ) { uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; m_model->updateMetaData( "cal3d_xml_material", "1" ); Model::ModelErrorE rval = Model::ERROR_NONE; // We're going to add a material whether successful or not. Materials are // referenced by index so the material needs to exist even if it's not valid. Model::Material * mat = Model::Material::get(); mat->m_type = Model::Material::MATTYPE_BLANK; // assume mat->m_name = m_modelPartName; // this should be a sensible name mat->m_filename = ""; // none by default if ( len > 0 ) { // don't worry about magic and version, just get to the material tag if ( findXElement( "MATERIAL" ) ) { std::string versionStr = readXAttribute( "VERSION" ); int xrfVersion = atoi( versionStr.c_str() ); if ( xrfVersion > m_maxXrfVersion ) m_maxXrfVersion = xrfVersion; Vector ambient( 0, 0, 0, 1 ); Vector diffuse( 1, 1, 1, 1 ); Vector specular( 0, 0, 0, 1 ); string matFile = ""; float shiny = 0.0f; if ( findXElement( "AMBIENT" ) ) { ambient = readAVector4( readXElement( "AMBIENT" ).c_str() ); ambient[0] /= 255.0; ambient[1] /= 255.0; ambient[2] /= 255.0; ambient[3] /= 255.0; } if ( findXElement( "DIFFUSE" ) ) { diffuse = readAVector4( readXElement( "DIFFUSE" ).c_str() ); diffuse[0] /= 255.0; diffuse[1] /= 255.0; diffuse[2] /= 255.0; diffuse[3] /= 255.0; } if ( findXElement( "SPECULAR" ) ) { specular = readAVector4( readXElement( "SPECULAR" ).c_str() ); specular[0] /= 255.0; specular[1] /= 255.0; specular[2] /= 255.0; specular[3] /= 255.0; } if ( findXElement( "SHININESS" ) ) { shiny = atof( readXElement( "SHININESS" ).c_str() ); } if ( findXElement( "MAP" ) ) { matFile = readAString( readXElement( "MAP" ).c_str() ); } if ( !matFile.empty() ) { mat->m_type = Model::Material::MATTYPE_TEXTURE; mat->m_filename = normalizePath(matFile.c_str(), m_currentPath.c_str()); } log_debug( "Reading material %d\n", m_model->getTextureCount() ); log_debug( " name %s\n", mat->m_name.c_str() ); log_debug( " file %s\n", mat->m_filename.c_str() ); log_debug( " diffuse %f,%f,%f\n", diffuse[0], diffuse[1], diffuse[2] ); log_debug( " ambient %f,%f,%f\n", ambient[0], ambient[1], ambient[2] ); log_debug( " specular %f,%f,%f\n", specular[0], specular[1], specular[2] ); log_debug( " shininess %f\n", shiny ); for ( int t = 0; t < 4; t++ ) { mat->m_diffuse[t] = diffuse[t]; mat->m_ambient[t] = ambient[t]; mat->m_specular[t] = specular[t]; mat->m_emissive[t] = 0.0f; } mat->m_shininess = shiny; } else { rval = Model::ERROR_BAD_MAGIC; log_error( "Could not find MATERIAL element in material file\n" ); } } else { rval = Model::ERROR_FILE_READ; log_error( "File is empty\n" ); } getMaterialList( m_model ).push_back( mat ); m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return rval; } Model::ModelErrorE Cal3dFilter::readAnimationFile( uint8_t * buf, size_t len ) { uint8_t * oldFileBuf = m_fileBuf; uint8_t * oldBufPos = m_bufPos; size_t oldLen = m_fileLength; m_fileBuf = buf; m_bufPos = buf; m_fileLength = len; Model::ModelErrorE rval = Model::ERROR_NONE; if ( memcmp( CAL3D_MAGIC_ANIMATION, m_bufPos, CAL3D_MAGIC_SIZE ) == 0 ) { m_bufPos += CAL3D_MAGIC_SIZE; int fileVersion = readBInt32(); float duration = readBFloat(); int numTracks = readBInt32(); if ( fileVersion > m_maxBinaryVersion ) m_maxBinaryVersion = fileVersion; log_debug( "anim version: %d (%x)\n", fileVersion, fileVersion ); if ( versionIsValid( FT_Animation, fileVersion ) ) { std::string name = m_modelPartName; bool compressed = false; if ( fileVersion >= CAL3D_COMP_ANIM_VERSION ) { int flags = readBInt32(); compressed = ( (flags & 1) != 0 ); } //name += "."; //name += m_modelPartExt; //log_debug( " tracks: %d\n", numTracks ); //log_debug( " seconds: %f\n", duration ); // create animation // NOTE: assume 30 fps, may want to be smarter about this m_anim = m_model->addAnimation( MODE, name.c_str() ); m_model->setAnimFPS( MODE, m_anim, 30.0 ); m_model->setAnimFrameCount( MODE, m_anim, timeToFrame(duration, 30.0) + 1); bool success = true; for ( int t = 0; success && t < numTracks; t++ ) { if ( compressed ) success = readBCompressedAnimTrack( duration ); else success = readBAnimTrack(); } } else { rval = Model::ERROR_UNSUPPORTED_VERSION; log_error( "Unsupported CAL3D animation version %d\n", fileVersion ); } } else { rval = Model::ERROR_BAD_MAGIC; log_error( "Bad magic in mesh file\n" ); } m_fileBuf = oldFileBuf; m_bufPos = oldBufPos; m_fileLength = oldLen; return rval; } Model::ModelErrorE Cal3dFilter::readFileToBuffer( const char * filename, uint8_t * & buf, size_t & len ) { buf = NULL; len = 0; Model::ModelErrorE err = Model::ERROR_NONE; DataSource * src = openInput( filename, err ); SourceCloser fc( src ); if ( err != Model::ERROR_NONE ) return err; len = src->getFileSize(); buf = new uint8_t[len]; src->readBytes( buf, len ); return err; } bool Cal3dFilter::readBBone() { log_debug( "Reading bone joint %d\n", m_model->getBoneJointCount() ); // Bone name std::string name; readBString( name ); log_debug( " name %s\n", name.c_str() ); // Bone position Vector trans = readBVector3(); Vector rotVec = readBVector4(); // Bone space translation Vector transLocal = readBVector3(); Vector rotQuatLocal = readBVector4(); log_debug( " pos = %.4f,%.4f,%.4f\n", trans[0], trans[1], trans[2] ); log_debug( " rot = %.4f,%.4f,%.4f,%.4f\n", rotVec[0], rotVec[1], rotVec[2], rotVec[3] ); log_debug( " lpos = %.4f,%.4f,%.4f\n", transLocal[0], transLocal[1], transLocal[2] ); log_debug( " lrot = %.4f,%.4f,%.4f,%.4f\n", rotQuatLocal[0], rotQuatLocal[1], rotQuatLocal[2], rotQuatLocal[3] ); // Parent bone joint int parent = readBInt32(); log_debug( " parent %d\n", parent ); // Children... we don't need this data, just skip past it int childCount = readBInt32(); //log_debug( " children %d\n", childCount ); for ( int c = 0; c < childCount; c++ ) { readBInt32(); } Quaternion rotQuat( rotVec ); Vector rot; // left-handed to right-handed conversion rotQuat = rotQuat.swapHandedness(); Matrix bmat; bmat.setRotationQuaternion( rotQuat ); bmat.setTranslation( trans ); // NOTE: This will not work if we're adding a joint with a parent // that is defined after us if ( parent >= 0 ) { if ( parent < m_model->getBoneJointCount() ) { Matrix pmat; m_model->getBoneJointAbsoluteMatrix( parent, pmat ); bmat = bmat * pmat; } else { log_error( " parent %d does not exist (yet)\n", parent ); } } //log_debug( " pre trans %f,%f,%f\n", // (float) trans[0], (float) trans[1], (float) trans[2] ); bmat.getTranslation( trans ); bmat.getRotation( rot ); //log_debug( " post trans %f,%f,%f\n", // (float) trans[0], (float) trans[1], (float) trans[2] ); /* log_debug( " rot %f,%f,%f\n", (float) rot[0], (float) rot[1], (float) rot[2] ); log_debug( " local trans %f,%f,%f\n", (float) transLocal[0], (float) transLocal[1], (float) transLocal[2] ); */ m_model->addBoneJoint( name.c_str(), trans[0], trans[1], trans[2], rot[0], rot[1], rot[2], parent ); return true; } bool Cal3dFilter::readBSubMesh() { log_debug( "Reading sub mesh %d\n", m_model->getGroupCount() ); int vertBase = m_model->getVertexCount(); int matId = readBInt32(); int numVerts = readBInt32(); int numFaces = readBInt32(); int numLOD = readBInt32(); int numSprings = readBInt32(); int numMaps = readBInt32(); string name = m_modelPartName; log_debug( " name: %s\n", name.c_str() ); log_debug( " material: %d\n", matId ); log_debug( " verts: %d\n", numVerts ); log_debug( " faces: %d\n", numFaces ); log_debug( " LOD steps: %d\n", numLOD ); log_debug( " springs: %d\n", numSprings ); log_debug( " maps: %d\n", numMaps ); if ( matId >= m_model->getTextureCount() ) { log_error( " material %d is out of range\n", matId ); } int group = m_model->addGroup( name.c_str() ); m_model->setGroupTextureId( group, matId ); UVList uvlist; // read vertices for ( int v = 0; v < numVerts; v++ ) { Vector pos = readBVector3(); readBVector3(); // normal, ignore it // LOD stuff, we ignore it readBInt32(); // vertex to collapse to readBInt32(); // number of faces collapsed // Add the vertex itself int vert = m_model->addVertex( pos[0], pos[1], pos[2] ); // Read UV for ( int m = 0; m < numMaps; m++ ) { float u = readBFloat(); float v = readBFloat(); // ignore unless it's the first one if ( m == 0 ) { // our vertices are per face, not per vertex // save this for faces section UVDataT uv; uv.u = u; uv.v = v; uvlist.push_back( uv ); } } // Read influences int numBones = readBInt32(); for ( int b = 0; b < numBones; b++ ) { int boneId = readBInt32(); float boneWeight = readBFloat(); if ( boneId < m_model->getBoneJointCount() ) { m_model->addVertexInfluence( vert, boneId, Model::IT_Custom, boneWeight ); } else { log_warning( " bone joint %d for vertex %d is out of range\n", boneId, vert ); } } if ( numSprings ) { // spring weight, only present if springs, ignore it readBFloat(); } } // read (and ignore) springs for ( int s = 0; s < numSprings; s++ ) { readBInt32(); // vertex 1 readBInt32(); // vertex 2 readBFloat(); // spring coefficient readBFloat(); // length of spring at rest (idle) } int vcount = m_model->getVertexCount(); int uvcount = uvlist.size(); // read faces for ( int f = 0; f < numFaces; f++ ) { int v1 = readBInt32(); int v2 = readBInt32(); int v3 = readBInt32(); if ( v1 < vcount && v2 < vcount && v3 < vcount ) { int tri = m_model->addTriangle( v1 + vertBase, v2 + vertBase, v3 + vertBase ); m_model->addTriangleToGroup( group, tri ); if ( v1 < uvcount && v2 < uvcount && v3 < uvcount ) { m_model->setTextureCoords( tri, 0, uvlist[v1].u, uvlist[v1].v ); m_model->setTextureCoords( tri, 1, uvlist[v2].u, uvlist[v2].v ); m_model->setTextureCoords( tri, 2, uvlist[v3].u, uvlist[v3].v ); } else { if ( uvcount > 0 ) { log_warning( "face vertex (%d,%d,%d) not in uvlist %d\n", v1, v2, v3, uvcount ); } } } else { log_error( "a vertex is out of range (%d,%d,%d) > %d\n", v1, v2, v3, vcount ); } } return true; } bool Cal3dFilter::readBAnimTrack() { //log_debug( "Reading animation track\n" ); int bone = readBInt32(); int numFrames = readBInt32(); //log_debug( " bone: %d\n", bone ); //log_debug( " frames: %d\n", numFrames ); for ( int f = 0; f < numFrames; f++ ) { float tsec = readBFloat(); // time in seconds Vector trans = readBVector3(); Vector rotVec = readBVector4(); Quaternion quat( rotVec ); quat = quat.swapHandedness(); Matrix m; m.setRotationQuaternion( quat ); m.setTranslation( trans ); // Cal3D anims are relative to joint parent // MM3D are relative to joint local position, convert Matrix inv; m_model->getBoneJointRelativeMatrix( bone, inv ); inv = inv.getInverse(); m = m * inv; double rot[3] = { 0, 0, 0 }; m.getRotation( rot ); m.getTranslation( trans ); int animFrame = timeToFrame( tsec, 30.0 ); m_model->setSkelAnimKeyframe( m_anim, animFrame, bone, true, rot[0], rot[1], rot[2] ); m_model->setSkelAnimKeyframe( m_anim, animFrame, bone, false, trans[0], trans[1], trans[2] ); } return true; } bool Cal3dFilter::readBCompressedAnimTrack( double duration ) { log_debug( "Reading animation track (compressed)\n" ); int bone = readBInt32(); int numFrames = readBInt32(); double scale[3] = { 1.0, 1.0, 1.0 }; double trans_min[3] = { 0.0, 0.0, 0.0 }; int comp_bits[3] = { 10, 11, 11 }; trans_min[0] = readBFloat(); trans_min[1] = readBFloat(); trans_min[2] = readBFloat(); scale[0] = readBFloat(); scale[1] = readBFloat(); scale[2] = readBFloat(); //log_debug( " bone: %d\n", bone ); //log_debug( " frames: %d\n", numFrames ); for ( int f = 0; f < numFrames; f++ ) { uint16_t qsec = readBInt16(); float tsec = (qsec / 65535.0) * duration; uint32_t trans_comp = readBInt32(); uint64_t rot_comp = readBUInt48(); log_debug( "compressed trans = %x rot = %" PRIx64 "\n", trans_comp, rot_comp ); Vector trans; Vector rotVec; for ( int i = 0; i < 3; ++i ) { trans[i] = ((2 << comp_bits[i]) - 1) & trans_comp; trans_comp = trans_comp >> comp_bits[i]; trans[i] /= (double) ((2 << comp_bits[i]) - 1); trans[i] *= scale[i]; } for ( int i = 0; i < 4; ++i ) { rotVec[i] = ((2 << 12) - 1) & rot_comp; rotVec[i] /= (double) ((2 << 12) - 1); rotVec[i] *= 2.0 - 1.0; } log_debug( "uncompressed quat = %f, %f, %f, %f \n", (float) rotVec[0], (float) rotVec[1], (float) rotVec[2], (float) rotVec[3] ); Quaternion quat( rotVec ); quat = quat.swapHandedness(); Matrix m; m.setRotationQuaternion( quat ); m.setTranslation( trans ); // Cal3D anims are relative to joint parent // MM3D are relative to joint local position, convert Matrix inv; m_model->getBoneJointRelativeMatrix( bone, inv ); inv = inv.getInverse(); m = m * inv; double rot[3] = { 0, 0, 0 }; m.getRotation( rot ); m.getTranslation( trans ); int animFrame = timeToFrame( tsec, 30.0 ); log_debug( "rotation (%f,%f,%f) translation (%f,%f,%f)\n", rot[0], rot[1], rot[2], trans[0], trans[1], trans[2] ); m_model->setSkelAnimKeyframe( m_anim, animFrame, bone, true, rot[0], rot[1], rot[2] ); m_model->setSkelAnimKeyframe( m_anim, animFrame, bone, false, trans[0], trans[1], trans[2] ); } return true; } uint64_t Cal3dFilter::readBUInt48() { uint32_t rval32 = readBInt32(); uint16_t rval16 = readBInt16(); uint64_t rval = ((uint64_t) rval16 << 32) | ((uint64_t) rval32); return rval; } int32_t Cal3dFilter::readBInt32() { int32_t rval = *((int32_t *) m_bufPos); m_bufPos += sizeof( rval ); rval = ltoh_u32( rval ); return rval; } int16_t Cal3dFilter::readBInt16() { int16_t rval = *((int16_t *) m_bufPos); m_bufPos += sizeof( rval ); rval = ltoh_u16( rval ); return rval; } uint8_t Cal3dFilter::readBUInt8() { uint8_t rval = *((uint8_t *) m_bufPos); m_bufPos += sizeof( rval ); return rval; } float Cal3dFilter::readBFloat() { float32_t rval = 0.0f; memcpy( &rval, m_bufPos, sizeof(rval ) ); m_bufPos += sizeof( rval ); rval = ltoh_float( rval ); return rval; } bool Cal3dFilter::readBString( std::string & str ) { size_t len = readBInt32(); size_t advance = len; while (len > 0 && m_bufPos[len-1] == '\0') --len; str.assign( (char *) m_bufPos, len ); //log_debug( "read string '%s' at %p\n", str.c_str(), m_bufPos ); m_bufPos += advance; return true; } bool Cal3dFilter::readBLine( std::string & str, size_t maxLen ) { size_t p = 0; size_t skipBytes = 0; for ( p = 0; p < maxLen; p++ ) { if ( m_bufPos[p] == '\n' ) { skipBytes = 1; break; } if ( m_bufPos[p] == '\r' && m_bufPos[p+1] == '\n' ) { skipBytes = 2; break; } } str.assign( (char *) m_bufPos, p ); m_bufPos += p + skipBytes; //log_debug( "size: %d, pos %d\n", maxLen, m_bufPos - m_fileBuf ); if ( (p + skipBytes) > 0 ) { return true; } else { return false; } } Vector Cal3dFilter::readBVector3() { Vector rval; rval[0] = readBFloat(); rval[1] = readBFloat(); rval[2] = readBFloat(); return rval; } Vector Cal3dFilter::readBVector4() { Vector rval; rval[0] = readBFloat(); rval[1] = readBFloat(); rval[2] = readBFloat(); rval[3] = readBFloat(); return rval; } Vector Cal3dFilter::readAVector3( const char * str ) { float fval[3]; sscanf( str, "%f %f %f", &fval[0], &fval[1], &fval[2] ); Vector rval; rval[0] = fval[0]; rval[1] = fval[1]; rval[2] = fval[2]; return rval; } Vector Cal3dFilter::readAVector4( const char * str ) { float fval[4]; sscanf( str, "%f %f %f %f", &fval[0], &fval[1], &fval[2], &fval[3] ); Vector rval; rval[0] = fval[0]; rval[1] = fval[1]; rval[2] = fval[2]; rval[3] = fval[3]; return rval; } std::string Cal3dFilter::readAString( const char * str ) { while ( isspace( str[0] ) ) { str++; } int len = strlen( str ) - 1; while ( len >= 0 && isspace( str[len] ) ) { len--; } len++; std::string rval; rval.assign( str, len ); return rval; } std::string Cal3dFilter::readLineFile( const char * str ) { const char * eq = strchr( str, '=' ); if ( eq ) { const char * start = strchr( eq, '"' ); if ( start ) { start += 1; const char * end = strchr( start, '"' ); if ( end ) { std::string rval; rval.assign( start, end - start ); return rval; } } else { // No quotes... uh... do... something eq++; // skip '=' while ( isspace( eq[0] ) ) { eq++; } return eq; } } return ""; } std::string Cal3dFilter::readLineKey( const char * str ) { const char * eq = strchr( str, '=' ); if ( eq ) { eq--; while (eq > str && isspace(*eq)) { eq--; } std::string rval; rval.assign( str, eq - str + 1 ); return rval; } return ""; } //------------------------------------------------------------------ // XML read functions // XXX: It is important to note that this XML reading functionality is // extremely limited. It is just barely good enough to read Cal3D // XML formats, and is not appropriate for any other uses. // // Why did I write my own instead of using a library? Primarily because // I don't want to increase external dependencies (mostly for portability // reasons). // // Why not use Qt's XML reading since MM3D is already using Qt? Because // the core MM3D model functionality is used in other projects that // do not link against Qt. // // So I wrote 3 XML parsing functions that are extremely limited use, // but "good enough" for what I need. // // Some things that are broken: // - No validity checks on input // - Can't ensure that an element is a sub-element (just looks at the // tags, not the document hierarchy) // - Attribute names must not be substrings of other attribute names // or attribute values in the same element (if there is you could // get a false attribute lookup) // - Text in an attribute value that looks like an element will be // parsed as an element // - It doesn't handle comments (elements in comments will be parsed) bool Cal3dFilter::findXElement( const char * tag ) { const char * buf = (const char *) m_fileBuf; size_t pos = m_bufPos - m_fileBuf; size_t tagLen = strlen( tag ); while ( pos < m_fileLength ) { if ( m_fileBuf[pos] == '<' ) { if ( strncasecmp( &buf[pos+1], tag, tagLen ) == 0) { m_bufPos = &m_fileBuf[pos]; //log_debug( "found tag %s at %p\n", tag, m_bufPos ); return true; } } pos++; } return false; } std::string Cal3dFilter::readXAttribute( const char * attr ) { const char * buf = (const char *) m_fileBuf; size_t pos = m_bufPos - m_fileBuf; size_t attrLen = strlen( attr ); size_t start = 0; bool inAttr = false; while ( pos < m_fileLength - 1 ) { if ( inAttr ) { if ( buf[pos] == '"' ) { std::string rval; rval.assign( &buf[ start ], pos - start ); //log_debug( "attribute: %s = %s\n", attr, rval.c_str() ); return rval; } } else { if ( strncmp( &buf[pos], attr, attrLen ) == 0 ) { pos += attrLen; while ( !inAttr && pos < m_fileLength - 1 ) { // NOTE: This assumes that no attribute name occures // as a sub-string of another attribute name or value. // It also assumes the '=' is present if ( buf[pos] == '"' ) { start = pos + 1; inAttr = true; // pos will get incremeneted at the end of the // outter loop to skip the quote char } else { pos++; } } } else if ( buf[pos] == '>' ) { return ""; } } pos++; } return ""; } std::string Cal3dFilter::readXElement( const char * tag ) { const char * buf = (const char *) m_fileBuf; size_t pos = m_bufPos - m_fileBuf; std::string endTag = std::string( "/" ) + tag; size_t tagLen = endTag.size(); bool inTag = true; size_t start = 0; while ( pos < m_fileLength - 1 ) { if ( inTag ) { if ( buf[pos] == '/' && buf[pos+1] == '>' ) { m_bufPos = &m_fileBuf[pos+2]; return ""; } else if ( buf[pos] == '>' ) { inTag = false; start = pos + 1; } } else { if ( buf[pos] == '<' ) { if ( strncasecmp( &buf[pos+1], endTag.c_str(), tagLen ) == 0) { m_bufPos = &m_fileBuf[pos]; //log_debug( "found end of tag %s at %p\n", tag, m_bufPos ); std::string rval; rval.assign( &buf[ start ], pos - start ); //log_debug( "tag data: %s\n", rval.c_str() ); return rval; } } } pos++; } return ""; } //------------------------------------------------------------------ // Format write functions Model::ModelErrorE Cal3dFilter::writeCfgFile( const char * filename, Model * model, ModelFilter::Options * o ) { Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; std::string base = m_modelPath + "/"; // Use dynamic cast to determine if the object is of the proper type // If not, create new one that we will delete later. // // We need to create one to make sure that the default options we // use in the filter match the default options presented to the // user in the dialog box. m_options = dynamic_cast< Cal3dOptions *>( o ); release_ptr freeOptions = NULL; if ( !m_options ) { freeOptions = static_cast( getDefaultOptions() ); m_options = freeOptions.get(); } m_model->updateMetaData( "cal3d_single_mesh_file", m_options->m_singleMeshFile ? "1" : "0" ); m_model->updateMetaData( "cal3d_xml_material", m_options->m_xmlMatFile ? "1" : "0" ); m_dst->writeString( "#\n# cal3d model configuration file\n#\n" ); m_dst->writeString( "# File written by Maverick Model 3D\n" ); m_dst->writeString( "# https://clover.com/mm3d\n\n" ); char value[64]; if ( m_model->getMetaData( "cal3d_path", value, sizeof(value) ) ) { m_dst->writePrintf( "path=%s\n", value ); } if ( m_model->getMetaData( "cal3d_scale", value, sizeof(value) ) ) { m_dst->writePrintf( "scale=%s\n", value ); } if ( m_model->getMetaData( "cal3d_rotate", value, sizeof(value) ) ) { m_dst->writePrintf( "rotate=%s\n", value ); } m_dst->writeString( "\n# --- Skeleton ---\n" ); std::string skelFile = replaceExtension( m_modelBaseName.c_str(), "csf" ); m_dst->writePrintf( "skeleton = %s\n\n", skelFile.c_str() ); writeSkeletonFile( (base + skelFile).c_str(), model ); // To write animations: // // * Make a map of filenames written (case-sensitive) // and map of animations written. // // * Run through meta data and write file if not already // in map. // // * Run through animations. If any not written, write them // and create an animation line in the Cal3D file. typedef std::map AnimFileMap; AnimFileMap fileWritten; std::vector animWritten; animWritten.resize( model->getAnimCount( MODE ) ); m_dst->writePrintf( "# --- Animations ---\n" ); std::string animFile; char keyStr[PATH_MAX]; char valueStr[PATH_MAX]; unsigned int mtcount = model->getMetaDataCount(); for ( unsigned int mt = 0; mt < mtcount; mt++ ) { model->getMetaData( mt, keyStr, PATH_MAX, valueStr, PATH_MAX ); if ( strncmp(keyStr, "animation_", 10 ) == 0 ) { animFile = valueStr; std::string animName = removeExtension( valueStr ); int a = findAnimation( animName ); if ( a >= 0 && fileWritten.find(animFile) == fileWritten.end() ) { writeAnimationFile( (base + animFile).c_str(), model, a ); animWritten[a] = true; fileWritten[animFile] = true; } m_dst->writePrintf( "animation = %s\n", animFile.c_str() ); } } unsigned int acount = model->getAnimCount( MODE ); for ( unsigned int a = 0; a < acount; a++ ) { if ( animWritten[a] == 0 ) { const char * animName = model->getAnimName( MODE, a ); animFile = animName; animFile = addExtension( animFile, "caf" ); m_dst->writePrintf( "animation = %s\n", animFile.c_str() ); writeAnimationFile( (base + animFile).c_str(), model, a ); animWritten[a] = 1; } } m_dst->writeString( "\n" ); // Create meshes split on groups, with UVs and // normals unique to each vertex MeshList ml; mesh_create_list( ml, model ); m_dst->writeString( "# --- Meshes ---\n" ); if ( (m_options == NULL) || m_options->m_singleMeshFile ) { std::string meshFile = replaceExtension( m_modelBaseName.c_str(), "cmf" ); m_dst->writePrintf( "mesh_file = %s\n", meshFile.c_str() ); writeMeshListFile( (base + meshFile).c_str(), model, ml ); } else { std::string meshName; std::string meshFile; unsigned int meshCount = ml.size(); unsigned int meshNum; typedef std::map FileMeshMap; FileMeshMap fmm; string groupName; for ( meshNum = 0; meshNum < meshCount; meshNum++ ) { groupName = m_model->getGroupName(ml[meshNum].group); _strtolower( groupName ); fmm[ groupName ].push_back( ml[meshNum] ); } // Write the meshes for each group for ( FileMeshMap::const_iterator fmm_it = fmm.begin(); fmm_it != fmm.end(); fmm_it++ ) { // Get a count of how many meshes make up this group // (probably 1, but you never know) string groupName = fmm_it->first; const MeshList & groupMeshList = fmm_it->second; // Create mesh name and filename strings string meshName = groupName; string meshFile = groupName + ".cmf"; _escapeCal3dName(meshName); _escapeFileName(meshFile); // Write mesh file m_dst->writePrintf( "mesh = %s\n", meshFile.c_str() ); writeMeshListFile( (base + meshFile).c_str(), model, groupMeshList ); } } m_dst->writeString( "\n# --- Materials ---\n" ); std::string matFile; unsigned int mcount = model->getTextureCount(); for ( unsigned int m = 0; m < mcount; m++ ) { const char * matName = model->getTextureName( m ); matFile = matName; if ( m_options && !m_options->m_xmlMatFile ) { matFile += ".crf"; writeMaterialFile( (base + matFile).c_str(), model, m ); } else { matFile += ".xrf"; writeXMaterialFile( (base + matFile).c_str(), model, m ); } m_dst->writePrintf( "material = %s\n", matFile.c_str() ); } m_dst->writeString( "\n" ); m_options = NULL; model->operationComplete( transll( QT_TRANSLATE_NOOP( "LowLevel", "Set meta data for Cal3D export" ) ).c_str() ); return err; } Model::ModelErrorE Cal3dFilter::writeCal3dFile( const char * filename, Model * model, ModelFilter::Options * o ) { Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; std::string base = m_modelPath + "/"; // Use dynamic cast to determine if the object is of the proper type // If not, create new one that we will delete later. // // We need to create one to make sure that the default options we // use in the filter match the default options presented to the // user in the dialog box. m_options = dynamic_cast< Cal3dOptions *>( o ); release_ptr freeOptions = NULL; if ( !m_options ) { freeOptions = static_cast( getDefaultOptions() ); m_options = freeOptions.get(); } m_model->updateMetaData( "cal3d_single_mesh_file", m_options->m_singleMeshFile ? "1" : "0" ); m_model->updateMetaData( "cal3d_xml_material", m_options->m_xmlMatFile ? "1" : "0" ); m_dst->writeString( "#\n# cal3d model configuration file\n#\n" ); m_dst->writeString( "# File written by Maverick Model 3D\n" ); m_dst->writeString( "# https://clover.com/mm3d\n\n" ); m_dst->writeString( "[model]\n" ); char value[64]; if ( m_model->getMetaData( "cal3d_path", value, sizeof(value) ) ) { m_dst->writePrintf( "path=\"%s\"\n", value ); } if ( m_model->getMetaData( "cal3d_scale", value, sizeof(value) ) ) { m_dst->writePrintf( "scale=\"%s\"\n", value ); } if ( m_model->getMetaData( "cal3d_rotate", value, sizeof(value) ) ) { m_dst->writePrintf( "rotate=\"%s\"\n", value ); } m_dst->writeString( "\n# --- Skeleton ---\n" ); std::string skelFile = replaceExtension( m_modelBaseName.c_str(), "csf" ); m_dst->writePrintf( "skeleton = \"%s\"\n\n", skelFile.c_str() ); writeSkeletonFile( (base + skelFile).c_str(), model ); // To write animations: // // * Make a map of filenames written (case-sensitive) // and map of animations written. // // * Run through meta data and write file if not already // in map. // // * Run through animations. If any not written, write them // and create an animation line in the Cal3D file. typedef std::map AnimFileMap; AnimFileMap fileWritten; std::vector animWritten; animWritten.resize( model->getAnimCount( MODE ) ); m_dst->writePrintf( "# --- Animations ---\n" ); std::string animFile; char keyStr[PATH_MAX]; char valueStr[PATH_MAX]; unsigned int mtcount = model->getMetaDataCount(); for ( unsigned int mt = 0; mt < mtcount; mt++ ) { model->getMetaData( mt, keyStr, PATH_MAX, valueStr, PATH_MAX ); if ( strncmp(keyStr, "animation_", 10 ) == 0 ) { animFile = valueStr; std::string animName = removeExtension( valueStr ); int a = findAnimation( animName ); if ( a >= 0 && fileWritten.find(animFile) == fileWritten.end() ) { writeAnimationFile( (base + animFile).c_str(), model, a ); animWritten[a] = true; fileWritten[animFile] = true; } m_dst->writePrintf( "%s = \"%s\"\n", keyStr, animFile.c_str() ); } } unsigned int acount = model->getAnimCount( MODE ); for ( unsigned int a = 0; a < acount; a++ ) { if ( animWritten[a] == 0 ) { const char * animName = model->getAnimName( MODE, a ); animFile = animName; animFile = addExtension( animFile, "caf" ); m_dst->writePrintf( "animation_%s = \"%s\"\n", animName, animFile.c_str() ); writeAnimationFile( (base + animFile).c_str(), model, a ); animWritten[a] = 1; } } m_dst->writeString( "\n" ); // Create meshes split on groups, with UVs and // normals unique to each vertex MeshList ml; mesh_create_list( ml, model ); m_dst->writeString( "# --- Meshes ---\n" ); if ( (m_options == NULL) || m_options->m_singleMeshFile ) { std::string meshFile = replaceExtension( m_modelBaseName.c_str(), "cmf" ); m_dst->writePrintf( "mesh_file = \"%s\"\n", meshFile.c_str() ); writeMeshListFile( (base + meshFile).c_str(), model, ml ); } else { std::string meshName; std::string meshFile; unsigned int meshCount = ml.size(); unsigned int meshNum; typedef std::map FileMeshMap; FileMeshMap fmm; string groupName; for ( meshNum = 0; meshNum < meshCount; meshNum++ ) { groupName = m_model->getGroupName(ml[meshNum].group); _strtolower( groupName ); fmm[ groupName ].push_back( ml[meshNum] ); } // Write the meshes for each group for ( FileMeshMap::const_iterator fmm_it = fmm.begin(); fmm_it != fmm.end(); fmm_it++ ) { // Get a count of how many meshes make up this group // (probably 1, but you never know) string groupName = fmm_it->first; const MeshList & groupMeshList = fmm_it->second; // Create mesh name and filename strings string meshName = groupName; string meshFile = groupName + ".cmf"; _escapeCal3dName(meshName); _escapeFileName(meshFile); // Write mesh file m_dst->writePrintf( "mesh_%s = \"%s\"\n", meshName.c_str(), meshFile.c_str() ); writeMeshListFile( (base + meshFile).c_str(), model, groupMeshList ); } } m_dst->writeString( "\n# --- Materials ---\n" ); std::string matFile; unsigned int mcount = model->getTextureCount(); for ( unsigned int m = 0; m < mcount; m++ ) { const char * matName = model->getTextureName( m ); matFile = matName; if ( m_options && !m_options->m_xmlMatFile ) { matFile += ".crf"; writeMaterialFile( (base + matFile).c_str(), model, m ); } else { matFile += ".xrf"; writeXMaterialFile( (base + matFile).c_str(), model, m ); } m_dst->writePrintf( "material_%s = \"%s\"\n", matName, matFile.c_str() ); } m_dst->writeString( "\n" ); m_options = NULL; model->operationComplete( transll( QT_TRANSLATE_NOOP( "LowLevel", "Set meta data for Cal3D export" ) ).c_str() ); return err; } Model::ModelErrorE Cal3dFilter::writeXMaterialFile( const char * filename, Model * model, unsigned int materialId ) { DataDest * oldDst = m_dst; Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; // Header int version = CAL3D_MIN_XVERSION; char versionStr[32]; if ( model->getMetaData( "cal3d_xrf_version", versionStr, sizeof(versionStr) ) ) { version = atoi(versionStr); } if ( version < CAL3D_NO_XHEADER_VERSION ) m_dst->writePrintf( "
\n", version ); Model::Material::MaterialTypeE type = model->getMaterialType( materialId ); // Start material element m_dst->writePrintf( "= CAL3D_NO_XHEADER_VERSION ) m_dst->writePrintf( " VERSION=\"%d\"", version ); m_dst->writePrintf( ">\n" ); // Material lighting properties float fval[4]; model->getTextureAmbient( materialId, fval ); writeXColor( "AMBIENT", fval ); model->getTextureDiffuse( materialId, fval ); writeXColor( "DIFFUSE", fval ); model->getTextureSpecular( materialId, fval ); writeXColor( "SPECULAR", fval ); model->getTextureShininess( materialId, fval[0] ); m_dst->writePrintf( " %f\n", fval[0] ); // Texture map if ( type == Model::Material::MATTYPE_TEXTURE ) { std::string textureFile = model->getTextureFilename( materialId ); std::string fullName; std::string fullPath; std::string baseName; normalizePath( filename, fullName, fullPath, baseName ); std::string relativeFile = getRelativePath( fullPath.c_str(), textureFile.c_str() ); m_dst->writePrintf( " %s\n", relativeFile.c_str() ); } // close material element m_dst->writePrintf( "\n" ); m_dst = oldDst; return err; } Model::ModelErrorE Cal3dFilter::writeSkeletonFile( const char * filename, Model * model ) { DataDest * oldDst = m_dst; Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; unsigned int bcount = model->getBoneJointCount(); int32_t version = CAL3D_MIN_BVERSION; char versionStr[32]; if ( model->getMetaData( "cal3d_binary_version", versionStr, sizeof(versionStr) ) ) { version = atoi(versionStr); } // Header m_dst->writeBytes( (uint8_t *) CAL3D_MAGIC_SKELETON, CAL3D_MAGIC_SIZE ); m_dst->write( version ); m_dst->write( (int32_t) bcount ); for ( unsigned int b = 0; b < bcount; b++ ) { writeBBone( b ); } m_dst = oldDst; return err; } Model::ModelErrorE Cal3dFilter::writeMeshFile( const char * filename, Model * model ) { // Create a list of all meshes MeshList ml; mesh_create_list( ml, model ); return writeMeshListFile( filename, model, ml ); } Model::ModelErrorE Cal3dFilter::writeMeshListFile( const char * filename, Model * model, const MeshList & meshList ) { DataDest * oldDst = m_dst; Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; int32_t version = CAL3D_MIN_BVERSION; char versionStr[32]; if ( model->getMetaData( "cal3d_binary_version", versionStr, sizeof(versionStr) ) ) { version = atoi(versionStr); } // Header m_dst->writeBytes( (uint8_t *) CAL3D_MAGIC_MESH, CAL3D_MAGIC_SIZE ); m_dst->write( version ); m_dst->write( (int32_t) meshList.size() ); // Save mesh to file log_debug( "writing mesh file %s\n", filename ); MeshList::const_iterator mit; for ( mit = meshList.begin(); mit != meshList.end(); mit++ ) { log_debug( " writing a mesh\n" ); writeBMesh( *mit ); } m_dst = oldDst; return err; } Model::ModelErrorE Cal3dFilter::writeMaterialFile( const char * filename, Model * model, unsigned int materialId ) { DataDest * oldDst = m_dst; Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; // Header m_dst->writeBytes( (uint8_t *) CAL3D_MAGIC_MATERIAL, CAL3D_MAGIC_SIZE ); int32_t version = CAL3D_MIN_BVERSION; char versionStr[32]; if ( model->getMetaData( "cal3d_binary_version", versionStr, sizeof(versionStr) ) ) { version = atoi(versionStr); } m_dst->write( version ); // Material lighting properties float fval[4]; model->getTextureAmbient( materialId, fval ); writeBColor( fval ); model->getTextureDiffuse( materialId, fval ); writeBColor( fval ); model->getTextureSpecular( materialId, fval ); writeBColor( fval ); model->getTextureShininess( materialId, fval[0] ); m_dst->write( (float32_t) fval[0] ); // Texture map if ( model->getMaterialType( materialId ) == Model::Material::MATTYPE_TEXTURE ) { m_dst->write( (int32_t) 1 ); std::string textureFile = model->getTextureFilename( materialId ); std::string fullName; std::string fullPath; std::string baseName; normalizePath( filename, fullName, fullPath, baseName ); std::string relativeFile = getRelativePath( fullPath.c_str(), textureFile.c_str() ); writeBString( relativeFile ); } else { m_dst->write( (int32_t) 0 ); } m_dst = oldDst; return err; } Model::ModelErrorE Cal3dFilter::writeAnimationFile( const char * filename, Model * model, unsigned int animationId ) { DataDest * oldDst = m_dst; Model::ModelErrorE err = Model::ERROR_NONE; m_dst = openOutput( filename, err ); DestCloser fc( m_dst ); if ( err != Model::ERROR_NONE ) return err; // get anim header info float fps = model->getAnimFPS( MODE, animationId ); unsigned int frameCount = model->getAnimFrameCount( MODE, animationId ); float32_t duration = (double) frameCount / fps; if ( frameCount > 0 ) { duration = ((double) frameCount - 1) / fps; } // build track list std::list tracks; bool found = false; double kf[3]; unsigned int bcount = model->getBoneJointCount(); for ( unsigned int b = 0; b < bcount; b++ ) { found = false; for ( unsigned int f = 0; !found && f < frameCount; f++ ) { if ( model->getSkelAnimKeyframe( animationId, f, b, false, kf[0], kf[1], kf[2] ) || model->getSkelAnimKeyframe( animationId, f, b, true, kf[0], kf[1], kf[2] ) ) { found = true; tracks.push_back( b ); } } } int32_t version = CAL3D_MIN_BVERSION; char versionStr[32]; if ( model->getMetaData( "cal3d_binary_version", versionStr, sizeof(versionStr) ) ) { version = atoi(versionStr); } // write header m_dst->writeBytes( (uint8_t *) CAL3D_MAGIC_ANIMATION, CAL3D_MAGIC_SIZE ); m_dst->write( version ); m_dst->write( duration ); m_dst->write( (int32_t) tracks.size() ); if ( version >= CAL3D_COMP_ANIM_VERSION ) { // Flags, no compression m_dst->write( (int32_t) 0 ); } // write tracks std::list::iterator it; for ( it = tracks.begin(); it != tracks.end(); it++ ) { writeBAnimTrack( animationId, *it ); } m_dst = oldDst; return err; } void Cal3dFilter::writeBBone( unsigned int b ) { std::string name = m_model->getBoneJointName( b ); writeBString( name ); Matrix m; m_model->getBoneJointAbsoluteMatrix( b, m ); Matrix pinv; int parent = m_model->getBoneJointParent( b ); if ( parent >= 0 ) { m_model->getBoneJointAbsoluteMatrix( parent, pinv ); pinv = pinv.getInverse(); } Matrix lm; lm = m * pinv; Vector trans; Quaternion rot; lm.getTranslation( trans ); lm.getRotationQuaternion( rot ); rot = rot.swapHandedness(); writeBVector3( trans ); // relative to parent writeBQuaternion( rot ); m = m.getInverse(); m.getTranslation( trans ); m.getRotationQuaternion( rot ); rot = rot.swapHandedness(); writeBVector3( trans ); // model space writeBQuaternion( rot ); // write parent m_dst->write( (int32_t) parent ); // find children std::list children; unsigned bcount = m_model->getBoneJointCount(); for ( unsigned int child = 0; child < bcount; child++ ) { int cparent = m_model->getBoneJointParent( child ); if ( (unsigned int) cparent == b ) { children.push_back( child ); } } // write children m_dst->write( (int32_t) children.size() ); std::list::iterator it; for ( it = children.begin(); it != children.end(); it++ ) { m_dst->write( (int32_t) *it ); } } void Cal3dFilter::writeBAnimTrack( unsigned int anim, unsigned int bone ) { float fps = m_model->getAnimFPS( MODE, anim ); unsigned int frameCount = m_model->getAnimFrameCount( MODE, anim ); std::list frames; double kf[3]; for ( unsigned int f = 0; f < frameCount; f++ ) { if ( m_model->getSkelAnimKeyframe( anim, f, bone, false, kf[0], kf[1], kf[2] ) || m_model->getSkelAnimKeyframe( anim, f, bone, true, kf[0], kf[1], kf[2] ) ) { frames.push_back( f ); } } // write track info m_dst->write( (int32_t) bone ); m_dst->write( (int32_t) frames.size() ); // write keyframe data std::list::iterator it; for ( it = frames.begin(); it != frames.end(); it++ ) { double frameTime = ((double) (*it)) / fps; m_dst->write( (float32_t) frameTime ); // MM3D allows one type of keyframe without the other, Cal3D requires // both rotation and translation for each keyframe. If we have one // and the other is missing, we must do interpolation here. Matrix m; m_model->interpSkelAnimKeyframeTime( anim, frameTime, true, bone, m ); Matrix rm; m_model->getBoneJointRelativeMatrix( bone, rm ); m = m * rm; Vector trans; Quaternion rot; m.getRotationQuaternion( rot ); m.getTranslation( trans ); rot = rot.swapHandedness(); writeBVector3( trans ); writeBQuaternion( rot ); } } void Cal3dFilter::writeBMesh( const Mesh & mesh ) { int materialId = -1; if ( mesh.group >= 0 ) { materialId = m_model->getGroupTextureId( mesh.group ); } m_dst->write( (int32_t) materialId ); m_dst->write( (int32_t) mesh.vertices.size() ); m_dst->write( (int32_t) mesh.faces.size() ); m_dst->write( (int32_t) 0 ); m_dst->write( (int32_t) 0 ); if ( materialId >= 0 ) { m_dst->write( (int32_t) 1 ); } else { m_dst->write( (int32_t) 0 ); } double coord[3]; Mesh::VertexList::const_iterator vit; for ( vit = mesh.vertices.begin(); vit != mesh.vertices.end(); vit++ ) { const Mesh::Vertex & mv = *vit; m_model->getVertexCoordsUnanimated( mv.v, coord ); m_dst->write( (float32_t) coord[0] ); m_dst->write( (float32_t) coord[1] ); m_dst->write( (float32_t) coord[2] ); m_dst->write( (float32_t) mv.norm[0] ); m_dst->write( (float32_t) mv.norm[1] ); m_dst->write( (float32_t) mv.norm[2] ); m_dst->write( (int32_t) -1 ); m_dst->write( (int32_t) 0 ); if ( materialId >= 0 ) { m_dst->write( (float32_t) mv.uv[0] ); m_dst->write( (float32_t) mv.uv[1] ); } Model::InfluenceList il; Model::InfluenceList::iterator it; m_model->getVertexInfluences( mv.v, il ); // Our weights don't always equal 100%, get total weigth so we can normalize double total = 0.0; for ( it = il.begin(); it != il.end(); it++ ) { total += it->m_weight; } // Don't allow negative weights, or divide by zero if ( total < 0.0005 ) { total = 1.0; } // Write out influence list m_dst->write( (int32_t) il.size() ); // number of influences for ( it = il.begin(); it != il.end(); it++ ) { m_dst->write( (int32_t) it->m_boneId ); // bone m_dst->write( (float32_t) (it->m_weight / total) ); // normalized weight } // No springs, don't need to write weight } // Write springs... we don't have any... don't write anything // Write faces Mesh::FaceList::const_iterator fit; for ( fit = mesh.faces.begin(); fit != mesh.faces.end(); fit++ ) { const Mesh::Face & mf = *fit; m_dst->write( (int32_t) mf.v[0] ); m_dst->write( (int32_t) mf.v[1] ); m_dst->write( (int32_t) mf.v[2] ); } } //------------------------------------------------------------------ // Binary write functions void Cal3dFilter::writeBVector3( const Vector & vec ) { m_dst->write( (float32_t) vec[0] ); m_dst->write( (float32_t) vec[1] ); m_dst->write( (float32_t) vec[2] ); } void Cal3dFilter::writeBQuaternion( const Quaternion & quat ) { m_dst->write( (float32_t) quat[0] ); m_dst->write( (float32_t) quat[1] ); m_dst->write( (float32_t) quat[2] ); m_dst->write( (float32_t) quat[3] ); } void Cal3dFilter::writeBString( const std::string & str ) { // Include NULL byte in write uint32_t len = str.size() + 1; m_dst->write( len ); m_dst->writeBytes( (uint8_t *) str.c_str(), len ); } void Cal3dFilter::writeBColor( const float * fval ) { uint32_t dval; for ( int i = 0; i < 4; i++ ) { dval = (uint32_t) (fval[i] * 255.0); if ( dval > 255 ) { dval = 255; } m_dst->write( (uint8_t) dval ); } } void Cal3dFilter::writeXColor( const char * tag, const float * fval ) { m_dst->writePrintf( " <%s>", tag ); uint32_t dval; for ( int i = 0; i < 4; i++ ) { dval = (uint32_t) (fval[i] * 255.0); if ( dval > 255 ) { dval = 255; } m_dst->writePrintf( "%d%s", dval, (i < 3) ? " " : "" ); } m_dst->writePrintf( "\n", tag ); } int Cal3dFilter::timeToFrame( double tsec, double fps ) { int frame = 0; while ( (frame / fps) < (tsec - 0.0005f) ) { frame++; } //log_debug( " time %f is frame %d\n", tsec, frame ); return frame; } int Cal3dFilter::findAnimation( const std::string& animName ) { int acount = m_model->getAnimCount( MODE ); for ( int a = 0; a < acount; a++ ) { if ( strcasecmp( animName.c_str(), m_model->getAnimName( MODE, a ) ) == 0 ) { return a; } } return -1; } #ifdef PLUGIN //------------------------------------------------------------------ // Plugin functions //------------------------------------------------------------------ PLUGIN_API bool plugin_init() { if ( s_filter == NULL ) { s_filter = new Cal3dFilter(); FilterManager * texmgr = FilterManager::getInstance(); texmgr->registerFilter( s_filter ); } log_debug( "CAL3D model filter plugin initialized\n" ); return true; } // The filter manager will delete our registered filter. // We have no other cleanup to do PLUGIN_API bool plugin_uninit() { s_filter = NULL; // FilterManager deletes filters log_debug( "CAL3D model filter plugin uninitialized\n" ); return true; } PLUGIN_API const char * plugin_mm3d_version() { return VERSION_STRING; } PLUGIN_API const char * plugin_version() { return "0.1.0"; } PLUGIN_API const char * plugin_desc() { return "CAL3D model filter"; } #endif // PLUGIN mm3d-1.3.15/src/libmm3d/cal3dfilter.h000066400000000000000000000151641466047437300171510ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CAL3DFILTER_H #define __CAL3DFILTER_H #include "modelfilter.h" #include "mesh.h" #include #include #include #include #include #include #include class Cal3dFilter : public ModelFilter { public: // The default values for the filter options are in // the constructor. class Cal3dOptions : public ModelFilter::Options { public: Cal3dOptions(); bool m_singleMeshFile; bool m_xmlMatFile; void setOptionsFromModel( Model * m ); protected: virtual ~Cal3dOptions(); // Use release() instead }; Cal3dFilter(); virtual ~Cal3dFilter(); Model::ModelErrorE readFile( Model * model, const char * const filename ); Model::ModelErrorE writeFile( Model * model, const char * const filename, ModelFilter::Options * o ); bool canRead( const char * filename ); bool canWrite( const char * filename ); bool canExport( const char * filename ); bool isSupported( const char * filename ); std::list< std::string > getReadTypes(); std::list< std::string > getWriteTypes(); ModelFilter::Options * getDefaultOptions() { return new Cal3dOptions; }; struct _UVData_t { float u; float v; }; typedef struct _UVData_t UVDataT; typedef std::vector< UVDataT > UVList; protected: enum _FileType_e { FT_Any, FT_Skeleton, FT_Animation, FT_Mesh, FT_Material, FT_MAX, }; typedef enum _FileType_e FileTypeE; static bool listHas( const std::list & l, const std::string & val ); static std::string addExtension( const std::string file, const std::string ext ); static bool versionIsValid( FileTypeE type, int version ); static bool xversionIsValid( FileTypeE type, int version ); // Sub file reads Model::ModelErrorE readSubFile( const char * filename ); Model::ModelErrorE readXSubFile( uint8_t * buf, size_t len ); Model::ModelErrorE readCal3dFile( uint8_t * buf, size_t len ); Model::ModelErrorE readSkeletonFile( uint8_t * buf, size_t len ); Model::ModelErrorE readMeshFile( uint8_t * buf, size_t len ); Model::ModelErrorE readMaterialFile( uint8_t * buf, size_t len ); Model::ModelErrorE readXMaterialFile( uint8_t * buf, size_t len ); Model::ModelErrorE readAnimationFile( uint8_t * buf, size_t len ); // Common read functions Model::ModelErrorE readFileToBuffer( const char * filename, uint8_t * & buf, size_t & len ); void freeFileBuffer( uint8_t * buf ); uint8_t readBUInt8(); int16_t readBInt16(); int32_t readBInt32(); uint64_t readBUInt48(); float readBFloat(); Vector readBVector3(); Vector readBVector4(); bool readBString( std::string & ); bool readBLine( std::string &, size_t maxLen ); // common XML reading functions bool findXElement( const char * tag ); // Finds an XML element by tag std::string readXAttribute( const char * attr ); // Read a specified attribute of the current element std::string readXElement( const char * tag ); // Read the contents of the XML tag (if any) Vector readAVector3( const char * str ); Vector readAVector4( const char * str ); std::string readAString( const char * str ); bool readBBone(); bool readBSubMesh(); bool readBAnimTrack(); bool readBCompressedAnimTrack( double duration ); std::string readLineKey( const char * str ); std::string readLineFile( const char * str ); // binary sub file writes Model::ModelErrorE writeCfgFile( const char * filename, Model * model, ModelFilter::Options * o ); Model::ModelErrorE writeCal3dFile( const char * filename, Model * model, ModelFilter::Options * o ); Model::ModelErrorE writeSkeletonFile( const char * filename, Model * model ); Model::ModelErrorE writeMeshFile( const char * filename, Model * model ); Model::ModelErrorE writeMeshListFile( const char * filename, Model * model, const MeshList & meshList ); Model::ModelErrorE writeMaterialFile( const char * filename, Model * model, unsigned int materialId ); Model::ModelErrorE writeAnimationFile( const char * filename, Model * model, unsigned int animationId ); // XML sub file writes Model::ModelErrorE writeXMaterialFile( const char * filename, Model * model, unsigned int materialId ); // Common binary write functions void writeBVector3( const Vector & vec ); void writeBQuaternion( const Quaternion & quat ); void writeBString( const std::string & ); void writeBColor( const float * fval ); void writeBBone( unsigned int b ); void writeBAnimTrack( unsigned int anim, unsigned int bone ); void writeBMesh( const Mesh & mesh ); // Common XML write functions void writeXColor( const char * tag, const float * fval ); int timeToFrame( double tsec, double fps ); int findAnimation( const std::string& animName ); Model * m_model; Cal3dOptions * m_options; DataDest * m_dst; uint8_t * m_bufPos; uint8_t * m_fileBuf; size_t m_fileLength; int m_anim; bool m_isBinary; bool m_isLittleEndian; int m_maxBinaryVersion; int m_maxXrfVersion; std::string m_modelPath; std::string m_modelBaseName; std::string m_modelFullName; std::string m_currentPath; std::string m_modelPartName; std::string m_modelPartExt; std::list m_formats; }; #endif // __CAL3DFILTER_H mm3d-1.3.15/src/libmm3d/cmdlinemgr.cc000066400000000000000000000146521466047437300172350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "cmdlinemgr.h" #include namespace { class FunctionOption : public CommandLineManager::Option { public: FunctionOption( char shortOption, const char * longOption, bool takesArg, CommandLineManager::OptionFunctionF optFunc ) : CommandLineManager::Option( shortOption, longOption, NULL, takesArg ), m_optFunc( optFunc ) { } virtual ~FunctionOption() { } void customParse( const char * arg ) { if ( m_optFunc ) m_optFunc( arg ); } private: CommandLineManager::OptionFunctionF m_optFunc; }; } // namespace CommandLineManager::Option::Option( char shortOption, const char * longOption, const char * defaultValue, bool takesArg ) : m_shortOption( shortOption ), m_longOption( longOption ), m_takesArg( takesArg ), m_isSpecified( false ), m_intValue( 0 ), m_stringValue( "" ) { if ( defaultValue ) { m_stringValue = defaultValue; m_intValue = atoi( defaultValue ); } } CommandLineManager::Option::~Option() { } void CommandLineManager::Option::parseOption( const char * arg ) { if ( arg ) { m_isSpecified = true; m_stringValue = arg; m_intValue = atoi( arg ); customParse( arg ); } } void CommandLineManager::Option::customParse( const char * arg ) { // Do nothing } CommandLineManager::CommandLineManager() : m_uniqueId( 0 ), m_firstArg( 1 ), m_error( NoError ), m_errorArg( 0 ) { } CommandLineManager::~CommandLineManager() { } bool CommandLineManager::parse( int argc, const char ** argv ) { for ( int a = 1; a < argc; a++ ) { const char * str = argv[a]; // If this string doesn't start with a dash, or is just a single // dash (stdin as file input), then we're done. if ( str[0] != '-' || str[1] == '\0' ) { m_firstArg = a; return true; } if ( strcmp("--", str) == 0 ) { // End of options m_firstArg = a + 1; return true; } Option * opt = NULL; const char * arg = NULL; int nextOpt = a; // It is an option if ( str[1] == '-' ) { // Long option std::string longOpt = &str[2]; const char * end = strchr( str, '=' ); if ( end ) { arg = end + 1; longOpt.resize( end - &str[2] ); } else { arg = argv[a+1]; nextOpt = a + 1; } opt = lookupOptionByLong( longOpt.c_str() ); } else { // Short option const char * end = strchr( str, '=' ); if ( end ) { arg = end + 1; } else { arg = argv[a+1]; nextOpt = a + 1; } opt = lookupOptionByShort( str[1] ); } if ( !opt ) { m_error = UnknownOption; m_errorArg = a; return false; } if ( opt->takesArgument() ) { a = nextOpt; if ( a >= argc ) { m_error = MissingArgument; m_errorArg = a - 1; return false; } } else { arg = ""; } opt->parseOption( arg ); } // Ran out of command line options, there are no arguments m_firstArg = argc; return true; } void CommandLineManager::addOption( int id, char shortOption, const char * longOption, const char * defaultValue, bool takesArg ) { m_optionList[ id ] = new Option( shortOption, longOption, defaultValue, takesArg ); } void CommandLineManager::addCustomOption( int id, Option * opt ) { m_optionList[ id ] = opt; } void CommandLineManager::addFunctionOption( int id, char shortOption, const char * longOption, bool takesArg, OptionFunctionF optFunc ) { m_optionList[ id ] = new FunctionOption( shortOption, longOption, takesArg, optFunc ); } bool CommandLineManager::isSpecified( int optionId ) const { const Option * opt = lookupOptionById( optionId ); if ( opt ) return opt->isSpecified(); return false; } int CommandLineManager::intValue( int optionId ) const { const Option * opt = lookupOptionById( optionId ); if ( opt ) return opt->intValue(); return 0; } const char * CommandLineManager::stringValue( int optionId ) const { const Option * opt = lookupOptionById( optionId ); if ( opt ) return opt->stringValue(); return ""; } int CommandLineManager::getUniqueId() { return --m_uniqueId; } const CommandLineManager::Option * CommandLineManager::lookupOptionById( int optionId ) const { OptionListT::const_iterator it = m_optionList.find( optionId ); if ( it != m_optionList.end() ) return it->second.get(); else return NULL; } CommandLineManager::Option * CommandLineManager::lookupOptionByShort( char shortOption ) { for ( OptionListT::iterator it = m_optionList.begin(); it != m_optionList.end(); ++it ) { const char opt = it->second->shortOption(); if ( opt != 0 && opt == shortOption ) { return it->second.get(); } } return NULL; } CommandLineManager::Option * CommandLineManager::lookupOptionByLong( const char * longOption ) { for ( OptionListT::iterator it = m_optionList.begin(); it != m_optionList.end(); ++it ) { const char * opt = it->second->longOption(); if ( opt && strcmp( opt, longOption ) == 0 ) { return it->second.get(); } } return NULL; } mm3d-1.3.15/src/libmm3d/cmdlinemgr.h000066400000000000000000000066421466047437300170770ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef CMDLINEMGR_H_INC__ #define CMDLINEMGR_H_INC__ #include #include #include "local_ptr.h" class CommandLineManager { public: class Option { public: Option( char shortOption, const char * longOption = NULL, const char * defaultValue = NULL, bool takesArg = false ); virtual ~Option(); char shortOption() const { return m_shortOption; } const char * longOption() const { return m_longOption; } bool takesArgument() const { return m_takesArg; } void parseOption(const char * arg); int intValue() const { return m_intValue; } const char * stringValue() const { return m_stringValue.c_str(); } bool isSpecified() const { return m_isSpecified; } protected: void setIntValue(int val) { m_intValue = val; } void setStringValue(const char * val) { m_stringValue = val; } virtual void customParse(const char * arg); private: char m_shortOption; const char * m_longOption; bool m_takesArg; bool m_isSpecified; int m_intValue; std::string m_stringValue; }; enum ErrorE { NoError, UnknownOption, MissingArgument, }; typedef void (*OptionFunctionF)(const char *); CommandLineManager(); virtual ~CommandLineManager(); bool parse( int argc, const char ** argv ); int firstArgument() const { return m_firstArg; } ErrorE error() const { return m_error; } int errorArgument() const { return m_errorArg; } void addOption( int id, char shortOption, const char * longOption = NULL, const char * defaultValue = NULL, bool takesArg = false ); void addCustomOption( int id, Option * opt ); void addFunctionOption( int id, char shortOption, const char * longOption, bool takesArg, OptionFunctionF optFunc ); bool isSpecified( int optionId ) const; int intValue( int optionId ) const; const char * stringValue( int optionId ) const; int getUniqueId(); private: typedef std::map< int, local_ptr
$alt
END_OF_HTML ; } elsif ($width == 100) { # Full coverage $graph_code = (<$alt END_OF_HTML ; } else { # Positive coverage $graph_code = (<$alt$alt END_OF_HTML ; } # Remove leading tabs from all lines $graph_code =~ s/^\t+//gm; chomp($graph_code); return($graph_code); } # # classify_coverage_rate(coverage_rate) # # Return the clasification ("Lo"|"Med"|"Hi") of a given coverage rate # sub classify_coverage_rate($) { my @pair = coverage_rate_helper($_[0]); return $pair[0]; } # # get_png_for_coverage_rate(coverage_rate) # # Return the name of the png corresponding to the given coverage rate # sub get_png_for_coverage_rate($) { my @pair = coverage_rate_helper($_[0]); return $pair[1]; } # # coverage_rate_helper(coverage_rate) # # Does the work for "classify_coverage_rate" and "get_png_for_coverage_rate". # sub coverage_rate_helper($) { my $rate = $_[0]; if ($rate < $med_limit) { return(("Lo","ruby.png")); } elsif ($rate < $hi_limit) { return(("Med","amber.png")); } else { return(("Hi","emerald.png")); } } # # write_html(filehandle, html_code) # # Write out HTML_CODE to FILEHANDLE while removing a leading tabulator mark # in each line of HTML_CODE. # sub write_html(*$) { local *HTML_HANDLE = $_[0]; my $html_code = $_[1]; # Remove leading tab from all lines $html_code =~ s/^\t//gm; print(HTML_HANDLE $html_code) or die("ERROR: cannot write HTML data ($!)\n"); } # # write_html_prolog(filehandle, base_dir, pagetitle) # # Write an HTML prolog common to all HTML files to FILEHANDLE. PAGETITLE will # be used as HTML page title. BASE_DIR contains a relative path which points # to the base directory. # sub write_html_prolog(*$$) { my $basedir = $_[1]; my $pagetitle = $_[2]; my $prolog; $prolog = $html_prolog; $prolog =~ s/\@pagetitle\@/$pagetitle/g; $prolog =~ s/\@basedir\@/$basedir/g; write_html($_[0], $prolog); } # # write_header_prolog(filehandle, base_dir) # # Write beginning of page header HTML code. # sub write_header_prolog(*$) { # ************************************************************* write_html($_[0], < $title END_OF_HTML ; # ************************************************************* } # # write_header_line(filehandle, item1, value1, [item2, value2]) # # Write a header line, containing of either one or two pairs "header item" # and "header value". # sub write_header_line(*$$;$$) { my $item1 = $_[1]; my $value1 = $_[2]; # Use GOTO to prevent indenting HTML with more than one tabs if (scalar(@_) > 3) { goto two_items } # ************************************************************* write_html($_[0], < END_OF_HTML ; return(); # ************************************************************* two_items: my $item2 = $_[3]; my $value2 = $_[4]; # ************************************************************* write_html($_[0], < END_OF_HTML ; # ************************************************************* } # # write_header_epilog(filehandle, base_dir) # # Write end of page header HTML code. # sub write_header_epilog(*$) { # ************************************************************* write_html($_[0], <
$item1: $value1
$item1: $value1 $item2: $value2
END_OF_HTML ; # ************************************************************* } # # write_header_legend(filehandle, type) # # Write a legend describing the coloring of the code coverage. # sub write_header_legend(*$) { my $type = $_[1]; # type of page being generated my $legend_body; # main summary or subdirectory summary if ($type == 0 or $type == 1) { $legend_body = (< Low: 0% to $med_limit% Medium: $med_limit% to $hi_limit% High: $hi_limit% to 100% END_OF_HTML ; } # source code elsif ($type == 2) { my $legend_highlight; $legend_highlight = ""; if ($highlight) { $legend_highlight = (< converted-only END_OF_HTML ; } $legend_body = (< not executed executed $legend_highlight END_OF_HTML ; } # description file ($type == 3) else { return(); } # ************************************************************* write_html($_[0], < Legend: $legend_body END_OF_HTML ; return(); # ************************************************************* } # # write_file_table_prolog(filehandle, left_heading, right_heading) # # Write heading for file table. # sub write_file_table_prolog(*$$) { # ************************************************************* write_html($_[0], < END_OF_HTML ; # ************************************************************* } # # write_file_table_entry(filehandle, cover_filename, cover_bar_graph, # cover_found, cover_hit) # # Write an entry of the file table. # sub write_file_table_entry(*$$$$) { my $rate; my $rate_string; my $classification = "Lo"; if ($_[3]>0) { $rate = $_[4] * 100 / $_[3]; $rate_string = sprintf("%.1f", $rate)." %"; $classification = classify_coverage_rate($rate); } else { $rate_string = "undefined"; } # ************************************************************* write_html($_[0], < END_OF_HTML ; # ************************************************************* } # # write_file_table_detail_heading(filehandle, left_heading, right_heading) # # Write heading for detail section in file table. # sub write_file_table_detail_heading(*$$) { # ************************************************************* write_html($_[0], < END_OF_HTML ; # ************************************************************* } # # write_file_table_detail_entry(filehandle, test_name, cover_found, cover_hit) # # Write entry for detail section in file table. # sub write_file_table_detail_entry(*$$$) { my $rate; my $name = $_[1]; if ($_[2]>0) { $rate = sprintf("%.1f", $_[3]*100/$_[2])." %"; } else { $rate = "undefined"; } if ($name =~ /^(.*),diff$/) { $name = $1." (converted)"; } if ($name eq "") { $name = "<unnamed>"; } # ************************************************************* write_html($_[0], < END_OF_HTML ; # ************************************************************* } # # write_file_table_epilog(filehandle) # # Write end of file table HTML code. # sub write_file_table_epilog(*) { # ************************************************************* write_html($_[0], <
END_OF_HTML ; # ************************************************************* } # # write_test_table_prolog(filehandle, table_heading) # # Write heading for test case description table. # sub write_test_table_prolog(*$) { # ************************************************************* write_html($_[0], <

$_[1] $_[2]
$_[1] $_[2] $rate_string $_[4] / $_[3] lines
$_[1] $_[2]
$name $rate $_[3] / $_[2] lines

$_[1]
END_OF_HTML ; # ************************************************************* } # # write_test_table_entry(filehandle, test_name, test_description) # # Write entry for the test table. # sub write_test_table_entry(*$$) { # ************************************************************* write_html($_[0], <$_[1] 
$_[2]

END_OF_HTML ; # ************************************************************* } # # write_test_table_epilog(filehandle) # # Write end of test description table HTML code. # sub write_test_table_epilog(*) { # ************************************************************* write_html($_[0], <

END_OF_HTML ; # ************************************************************* } # # write_source_prolog(filehandle) # # Write start of source code table. # sub write_source_prolog(*) { # ************************************************************* write_html($_[0], <
END_OF_HTML
	;

	# *************************************************************
}


#
# write_source_line(filehandle, line_num, source, hit_count, converted)
#
# Write formatted source code line. Return a line in a format as needed
# by gen_png()
#

sub write_source_line(*$$$$)
{
	my $source_format;
	my $count;
	my $result;
	my $anchor_start = "";
	my $anchor_end = "";

	if (!(defined$_[3]))
	{
		$result		= "";
		$source_format	= "";
		$count		= " "x15;
	}
	elsif ($_[3] == 0)
	{
		$result		= $_[3];
		$source_format	= '';
		$count		= sprintf("%15d", $_[3]);
	}
	elsif ($_[4] && defined($highlight))
	{
		$result		= "*".$_[3];
		$source_format	= '';
		$count		= sprintf("%15d", $_[3]);
	}
	else
	{
		$result		= $_[3];
		$source_format	= '';
		$count		= sprintf("%15d", $_[3]);
	}

	$result .= ":".$_[2];

	# Write out a line number navigation anchor every $nav_resolution
	# lines if necessary
	if ($frames && (($_[1] - 1) % $nav_resolution == 0))
	{
		$anchor_start	= "";
		$anchor_end	= "";
	}


	# *************************************************************

	write_html($_[0],
		   $anchor_start.
		   ''.sprintf("%8d", $_[1]).
		   " $source_format$count : ".
		   escape_html($_[2]).($source_format?"":"").
		   $anchor_end."\n");

	# *************************************************************

	return($result);
}


#
# write_source_epilog(filehandle)
#
# Write end of source code table.
#

sub write_source_epilog(*)
{
	# *************************************************************

	write_html($_[0], <
	      
	    
	  
	  
END_OF_HTML ; # ************************************************************* } # # write_html_epilog(filehandle, base_dir[, break_frames]) # # Write HTML page footer to FILEHANDLE. BREAK_FRAMES should be set when # this page is embedded in a frameset, clicking the URL link will then # break this frameset. # sub write_html_epilog(*$;$) { my $basedir = $_[1]; my $break_code = ""; my $epilog; if (defined($_[2])) { $break_code = " target=\"_parent\""; } # ************************************************************* write_html($_[0], < Generated by: $lcov_version
END_OF_HTML ; $epilog = $html_epilog; $epilog =~ s/\@basedir\@/$basedir/g; write_html($_[0], $epilog); } # # write_frameset(filehandle, basedir, basename, pagetitle) # # sub write_frameset(*$$$) { my $frame_width = $overview_width + 40; # ************************************************************* write_html($_[0], < $_[3] <center>Frames not supported by your browser!<br></center> END_OF_HTML ; # ************************************************************* } # # sub write_overview_line(filehandle, basename, line, link) # # sub write_overview_line(*$$$) { my $y1 = $_[2] - 1; my $y2 = $y1 + $nav_resolution - 1; my $x2 = $overview_width - 1; # ************************************************************* write_html($_[0], < END_OF_HTML ; # ************************************************************* } # # write_overview(filehandle, basedir, basename, pagetitle, lines) # # sub write_overview(*$$$$) { my $index; my $max_line = $_[4] - 1; my $offset; # ************************************************************* write_html($_[0], < $_[3] END_OF_HTML ; # ************************************************************* # Make $offset the next higher multiple of $nav_resolution $offset = ($nav_offset + $nav_resolution - 1) / $nav_resolution; $offset = sprintf("%d", $offset ) * $nav_resolution; # Create image map for overview image for ($index = 1; $index <= $_[4]; $index += $nav_resolution) { # Enforce nav_offset if ($index < $offset + 1) { write_overview_line($_[0], $_[2], $index, 1); } else { write_overview_line($_[0], $_[2], $index, $index - $offset); } } # ************************************************************* write_html($_[0], <
Top

Overview
END_OF_HTML ; # ************************************************************* } # # write_header(filehandle, type, trunc_file_name, rel_file_name, lines_found, # lines_hit) # # Write a complete standard page header. TYPE may be (0, 1, 2, 3) # corresponding to (directory view header, file view header, source view # header, test case description header) # sub write_header(*$$$$$) { local *HTML_HANDLE = $_[0]; my $type = $_[1]; my $trunc_name = $_[2]; my $rel_filename = $_[3]; my $lines_found = $_[4]; my $lines_hit = $_[5]; my $base_dir; my $view; my $test; my $rate; my $base_name; # Calculate coverage rate if ($lines_found>0) { $rate = sprintf("%.1f", $lines_hit * 100 / $lines_found)." %"; } else { $rate = "-"; } $base_name = basename($rel_filename); # Prepare text for "current view" field if ($type == 0) { # Main overview $base_dir = ""; $view = $overview_title; } elsif ($type == 1) { # Directory overview $base_dir = get_relative_base_path($rel_filename); $view = "". "$overview_title - $trunc_name"; } elsif ($type == 2) { # File view my $dir_name = dirname($rel_filename); $base_dir = get_relative_base_path($dir_name); if ($frames) { # Need to break frameset when clicking any of these # links $view = "$overview_title - ". "". "$dir_name - $base_name"; } else { $view = "". "$overview_title - ". "". "$dir_name - $base_name"; } } elsif ($type == 3) { # Test description header $base_dir = ""; $view = "". "$overview_title - test case descriptions"; } # Prepare text for "test" field $test = escape_html($test_title); # Append link to test description page if available if (%test_description && ($type != 3)) { if ($frames && ($type == 2)) { # Need to break frameset when clicking this link $test .= " ( ". "view test case descriptions )"; } else { $test .= " ( ". "view test case descriptions )"; } } # Write header write_header_prolog(*HTML_HANDLE, $base_dir); write_header_line(*HTML_HANDLE, "Current view", $view); write_header_line(*HTML_HANDLE, "Test", $test); write_header_line(*HTML_HANDLE, "Date", $date, "Instrumented lines", $lines_found); write_header_line(*HTML_HANDLE, "Code covered", $rate, "Executed lines", $lines_hit); if ($legend) { write_header_legend(*HTML_HANDLE, $type); } write_header_epilog(*HTML_HANDLE, $base_dir); } # # split_filename(filename) # # Return (path, filename, extension) for a given FILENAME. # sub split_filename($) { if (!$_[0]) { return(); } my @path_components = split('/', $_[0]); my @file_components = split('\.', pop(@path_components)); my $extension = pop(@file_components); return (join("/",@path_components), join(".",@file_components), $extension); } # # write_file_table(filehandle, base_dir, overview, testhash, fileview) # # Write a complete file table. OVERVIEW is a reference to a hash containing # the following mapping: # # filename -> "lines_found,lines_hit,page_link" # # TESTHASH is a reference to the following hash: # # filename -> \%testdata # %testdata: name of test affecting this file -> \%testcount # %testcount: line number -> execution count for a single test # # Heading of first column is "Filename" if FILEVIEW is true, "Directory name" # otherwise. # sub write_file_table(*$$$$) { local *HTML_HANDLE = $_[0]; my $base_dir = $_[1]; my %overview = %{$_[2]}; my %testhash = %{$_[3]}; my $fileview = $_[4]; my $filename; my $bar_graph; my $hit; my $found; my $page_link; my $testname; my $testdata; my $testcount; my %affecting_tests; my $coverage_heading = "Coverage"; # Provide a link to details/non-detail list if this is directory # overview and we are supposed to create a detail view if (($base_dir ne "") && $show_details) { if (%testhash) { # This is the detail list, provide link to standard # list $coverage_heading .= " ( hide ". "details )"; } else { # This is the standard list, provide link to detail # list $coverage_heading .= " ( show ". "details )"; } } write_file_table_prolog(*HTML_HANDLE, $fileview ? "Filename" : "Directory name", $coverage_heading); foreach $filename (sort(keys(%overview))) { ($found, $hit, $page_link) = split(",", $overview{$filename}); $bar_graph = get_bar_graph_code($base_dir, $found, $hit); $testdata = $testhash{$filename}; # Add anchor tag in case a page link is provided if ($page_link) { $filename = "$filename"; } write_file_table_entry(*HTML_HANDLE, $filename, $bar_graph, $found, $hit); # Check whether we should write test specific coverage # as well if (!($show_details && $testdata)) { next; } # Filter out those tests that actually affect this file %affecting_tests = %{ get_affecting_tests($testdata) }; # Does any of the tests affect this file at all? if (!%affecting_tests) { next; } # Write test details for this entry write_file_table_detail_heading(*HTML_HANDLE, "Test name", "Lines hit"); foreach $testname (keys(%affecting_tests)) { ($found, $hit) = split(",", $affecting_tests{$testname}); # Insert link to description of available if ($test_description{$testname}) { $testname = "". "$testname"; } write_file_table_detail_entry(*HTML_HANDLE, $testname, $found, $hit); } } write_file_table_epilog(*HTML_HANDLE); } # # get_found_and_hit(hash) # # Return the count for entries (found) and entries with an execution count # greater than zero (hit) in a hash (linenumber -> execution count) as # a list (found, hit) # sub get_found_and_hit($) { my %hash = %{$_[0]}; my $found = 0; my $hit = 0; # Calculate sum $found = 0; $hit = 0; foreach (keys(%hash)) { $found++; if ($hash{$_}>0) { $hit++; } } return ($found, $hit); } # # get_affecting_tests(hashref) # # HASHREF contains a mapping filename -> (linenumber -> exec count). Return # a hash containing mapping filename -> "lines found, lines hit" for each # filename which has a nonzero hit count. # sub get_affecting_tests($) { my %hash = %{$_[0]}; my $testname; my $testcount; my %result; my $found; my $hit; foreach $testname (keys(%hash)) { # Get (line number -> count) hash for this test case $testcount = $hash{$testname}; # Calculate sum ($found, $hit) = get_found_and_hit($testcount); if ($hit>0) { $result{$testname} = "$found,$hit"; } } return(\%result); } # # write_source(filehandle, source_filename, count_data, checksum_data, # converted_data) # # Write an HTML view of a source code file. Returns a list containing # data as needed by gen_png(). # # Die on error. # sub write_source($$$$$) { local *HTML_HANDLE = $_[0]; local *SOURCE_HANDLE; my $source_filename = $_[1]; my %count_data; my $line_number; my @result; my $checkdata = $_[3]; my $converted = $_[4]; if ($_[2]) { %count_data = %{$_[2]}; } open(SOURCE_HANDLE, "<".$source_filename) or die("ERROR: cannot open $source_filename for reading!\n"); write_source_prolog(*HTML_HANDLE); for ($line_number = 1; ; $line_number++) { chomp($_); # Source code matches coverage data? if (defined($checkdata->{$line_number}) && ($checkdata->{$line_number} ne md5_base64($_))) { die("ERROR: checksum mismatch at $source_filename:". "$line_number\n"); } push (@result, write_source_line(HTML_HANDLE, $line_number, $_, $count_data{$line_number}, $converted->{$line_number})); } close(SOURCE_HANDLE); write_source_epilog(*HTML_HANDLE); return(@result); } # # info(printf_parameter) # # Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag # is not set. # sub info(@) { if (!$quiet) { # Print info string printf(@_); } } # # subtract_counts(data_ref, base_ref) # sub subtract_counts($$) { my %data = %{$_[0]}; my %base = %{$_[1]}; my $line; my $data_count; my $base_count; my $hit = 0; my $found = 0; foreach $line (keys(%data)) { $found++; $data_count = $data{$line}; $base_count = $base{$line}; if (defined($base_count)) { $data_count -= $base_count; # Make sure we don't get negative numbers if ($data_count<0) { $data_count = 0; } } $data{$line} = $data_count; if ($data_count > 0) { $hit++; } } return (\%data, $found, $hit); } # # add_counts(data1_ref, data2_ref) # # DATA1_REF and DATA2_REF are references to hashes containing a mapping # # line number -> execution count # # Return a list (RESULT_REF, LINES_FOUND, LINES_HIT) where RESULT_REF # is a reference to a hash containing the combined mapping in which # execution counts are added. # sub add_counts($$) { my %data1 = %{$_[0]}; # Hash 1 my %data2 = %{$_[1]}; # Hash 2 my %result; # Resulting hash my $line; # Current line iteration scalar my $data1_count; # Count of line in hash1 my $data2_count; # Count of line in hash2 my $found = 0; # Total number of lines found my $hit = 0; # Number of lines with a count > 0 foreach $line (keys(%data1)) { $data1_count = $data1{$line}; $data2_count = $data2{$line}; # Add counts if present in both hashes if (defined($data2_count)) { $data1_count += $data2_count; } # Store sum in %result $result{$line} = $data1_count; $found++; if ($data1_count > 0) { $hit++; } } # Add lines unique to data2 foreach $line (keys(%data2)) { # Skip lines already in data1 if (defined($data1{$line})) { next; } # Copy count from data2 $result{$line} = $data2{$line}; $found++; if ($result{$line} > 0) { $hit++; } } return (\%result, $found, $hit); } # # apply_baseline(data_ref, baseline_ref) # # Subtract the execution counts found in the baseline hash referenced by # BASELINE_REF from actual data in DATA_REF. # sub apply_baseline($$) { my %data_hash = %{$_[0]}; my %base_hash = %{$_[1]}; my $filename; my $testname; my $data; my $data_testdata; my $data_funcdata; my $data_checkdata; my $data_count; my $base; my $base_testdata; my $base_checkdata; my $base_count; my $sumcount; my $found; my $hit; foreach $filename (keys(%data_hash)) { # Get data set for data and baseline $data = $data_hash{$filename}; $base = $base_hash{$filename}; # Get set entries for data and baseline ($data_testdata, undef, $data_funcdata, $data_checkdata) = get_info_entry($data); ($base_testdata, $base_count, undef, $base_checkdata) = get_info_entry($base); # Check for compatible checksums merge_checksums($data_checkdata, $base_checkdata, $filename); # Sumcount has to be calculated anew $sumcount = {}; # For each test case, subtract test specific counts foreach $testname (keys(%{$data_testdata})) { # Get counts of both data and baseline $data_count = $data_testdata->{$testname}; $hit = 0; ($data_count, undef, $hit) = subtract_counts($data_count, $base_count); # Check whether this test case did hit any line at all if ($hit > 0) { # Write back resulting hash $data_testdata->{$testname} = $data_count; } else { # Delete test case which did not impact this # file delete($data_testdata->{$testname}); } # Add counts to sum of counts ($sumcount, $found, $hit) = add_counts($sumcount, $data_count); } # Write back resulting entry set_info_entry($data, $data_testdata, $sumcount, $data_funcdata, $data_checkdata, $found, $hit); $data_hash{$filename} = $data; } return (\%data_hash); } # # remove_unused_descriptions() # # Removes all test descriptions from the global hash %test_description which # are not present in %info_data. # sub remove_unused_descriptions() { my $filename; # The current filename my %test_list; # Hash containing found test names my $test_data; # Reference to hash test_name -> count_data my $before; # Initial number of descriptions my $after; # Remaining number of descriptions $before = scalar(keys(%test_description)); foreach $filename (keys(%info_data)) { ($test_data) = get_info_entry($info_data{$filename}); foreach (keys(%{$test_data})) { $test_list{$_} = ""; } } # Remove descriptions for tests which are not in our list foreach (keys(%test_description)) { if (!defined($test_list{$_})) { delete($test_description{$_}); } } $after = scalar(keys(%test_description)); if ($after < $before) { info("Removed ".($before - $after). " unused descriptions, $after remaining.\n"); } } # # merge_checksums(ref1, ref2, filename) # # REF1 and REF2 are references to hashes containing a mapping # # line number -> checksum # # Merge checksum lists defined in REF1 and REF2 and return reference to # resulting hash. Die if a checksum for a line is defined in both hashes # but does not match. # sub merge_checksums($$$) { my $ref1 = $_[0]; my $ref2 = $_[1]; my $filename = $_[2]; my %result; my $line; foreach $line (keys(%{$ref1})) { if (defined($ref2->{$line}) && ($ref1->{$line} ne $ref2->{$line})) { die("ERROR: checksum mismatch at $filename:$line\n"); } $result{$line} = $ref1->{$line}; } foreach $line (keys(%{$ref2})) { $result{$line} = $ref2->{$line}; } return \%result; } # # combine_info_entries(entry_ref1, entry_ref2, filename) # # Combine .info data entry hashes referenced by ENTRY_REF1 and ENTRY_REF2. # Return reference to resulting hash. # sub combine_info_entries($$$) { my $entry1 = $_[0]; # Reference to hash containing first entry my $testdata1; my $sumcount1; my $funcdata1; my $checkdata1; my $entry2 = $_[1]; # Reference to hash containing second entry my $testdata2; my $sumcount2; my $funcdata2; my $checkdata2; my %result; # Hash containing combined entry my %result_testdata; my $result_sumcount = {}; my %result_funcdata; my $lines_found; my $lines_hit; my $testname; my $filename = $_[2]; # Retrieve data ($testdata1, $sumcount1, $funcdata1, $checkdata1) = get_info_entry($entry1); ($testdata2, $sumcount2, $funcdata2, $checkdata2) = get_info_entry($entry2); # Merge checksums $checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename); # Combine funcdata foreach (keys(%{$funcdata1})) { $result_funcdata{$_} = $funcdata1->{$_}; } foreach (keys(%{$funcdata2})) { $result_funcdata{$_} = $funcdata2->{$_}; } # Combine testdata foreach $testname (keys(%{$testdata1})) { if (defined($testdata2->{$testname})) { # testname is present in both entries, requires # combination ($result_testdata{$testname}) = add_counts($testdata1->{$testname}, $testdata2->{$testname}); } else { # testname only present in entry1, add to result $result_testdata{$testname} = $testdata1->{$testname}; } # update sum count hash ($result_sumcount, $lines_found, $lines_hit) = add_counts($result_sumcount, $result_testdata{$testname}); } foreach $testname (keys(%{$testdata2})) { # Skip testnames already covered by previous iteration if (defined($testdata1->{$testname})) { next; } # testname only present in entry2, add to result hash $result_testdata{$testname} = $testdata2->{$testname}; # update sum count hash ($result_sumcount, $lines_found, $lines_hit) = add_counts($result_sumcount, $result_testdata{$testname}); } # Calculate resulting sumcount # Store result set_info_entry(\%result, \%result_testdata, $result_sumcount, \%result_funcdata, $checkdata1, $lines_found, $lines_hit); return(\%result); } # # combine_info_files(info_ref1, info_ref2) # # Combine .info data in hashes referenced by INFO_REF1 and INFO_REF2. Return # reference to resulting hash. # sub combine_info_files($$) { my %hash1 = %{$_[0]}; my %hash2 = %{$_[1]}; my $filename; foreach $filename (keys(%hash2)) { if ($hash1{$filename}) { # Entry already exists in hash1, combine them $hash1{$filename} = combine_info_entries($hash1{$filename}, $hash2{$filename}, $filename); } else { # Entry is unique in both hashes, simply add to # resulting hash $hash1{$filename} = $hash2{$filename}; } } return(\%hash1); } # # apply_prefix(filename, prefix) # # If FILENAME begins with PREFIX, remove PREFIX from FILENAME and return # resulting string, otherwise return FILENAME. # sub apply_prefix($$) { my $filename = $_[0]; my $prefix = $_[1]; if (defined($prefix) && ($prefix ne "")) { if ($filename =~ /^\Q$prefix\E\/(.*)$/) { return substr($filename, length($prefix) + 1); } } return $filename; } # # system_no_output(mode, parameters) # # Call an external program using PARAMETERS while suppressing depending on # the value of MODE: # # MODE & 1: suppress STDOUT # MODE & 2: suppress STDERR # # Return 0 on success, non-zero otherwise. # sub system_no_output($@) { my $mode = shift; my $result; local *OLD_STDERR; local *OLD_STDOUT; # Save old stdout and stderr handles ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT"); ($mode & 2) && open(OLD_STDERR, ">>&STDERR"); # Redirect to /dev/null ($mode & 1) && open(STDOUT, ">/dev/null"); ($mode & 2) && open(STDERR, ">/dev/null"); system(@_); $result = $?; # Close redirected handles ($mode & 1) && close(STDOUT); ($mode & 2) && close(STDERR); # Restore old handles ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT"); ($mode & 2) && open(STDERR, ">>&OLD_STDERR"); return $result; } # # read_config(filename) # # Read configuration file FILENAME and return a reference to a hash containing # all valid key=value pairs found. # sub read_config($) { my $filename = $_[0]; my %result; my $key; my $value; local *HANDLE; if (!open(HANDLE, "<$filename")) { warn("WARNING: cannot read configuration file $filename\n"); return undef; } while () { chomp; # Skip comments s/#.*//; # Remove leading blanks s/^\s+//; # Remove trailing blanks s/\s+$//; next unless length; ($key, $value) = split(/\s*=\s*/, $_, 2); if (defined($key) && defined($value)) { $result{$key} = $value; } else { warn("WARNING: malformed statement in line $. ". "of configuration file $filename\n"); } } close(HANDLE); return \%result; } # # apply_config(REF) # # REF is a reference to a hash containing the following mapping: # # key_string => var_ref # # where KEY_STRING is a keyword and VAR_REF is a reference to an associated # variable. If the global configuration hash CONFIG contains a value for # keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. # sub apply_config($) { my $ref = $_[0]; foreach (keys(%{$ref})) { if (defined($config->{$_})) { ${$ref->{$_}} = $config->{$_}; } } } # # get_html_prolog(FILENAME) # # If FILENAME is defined, return contents of file. Otherwise return default # HTML prolog. Die on error. # sub get_html_prolog($) { my $filename = $_[0]; my $result = ""; if (defined($filename)) { local *HANDLE; open(HANDLE, "<".$filename) or die("ERROR: cannot open html prolog $filename!\n"); while () { $result .= $_; } close(HANDLE); } else { $result = < \@pagetitle\@ END_OF_HTML ; } return $result; } # # get_html_epilog(FILENAME) # # If FILENAME is defined, return contents of file. Otherwise return default # HTML epilog. Die on error. # sub get_html_epilog($) { my $filename = $_[0]; my $result = ""; if (defined($filename)) { local *HANDLE; open(HANDLE, "<".$filename) or die("ERROR: cannot open html epilog $filename!\n"); while () { $result .= $_; } close(HANDLE); } else { $result = < END_OF_HTML ; } return $result; } mm3d-1.3.15/src/tests/libmm3d/glmath_test.cc000066400000000000000000001273151466047437300205720ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests glmath.h #include #include #include #include #include #include "glmath.h" #include "log.h" #include "test_common.h" double TOLERANCE = 0.001; class GlmathTest : public QObject { Q_OBJECT private slots: void initTestCase() { log_enable_debug( false ); } void testVectorInit() { { Vector v; QVERIFY_TRUE( fabs(v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(1.0 - v.get(3)) < TOLERANCE ); } { // 4th element is ignored double array[4] = { 3, 4, 5, 6 }; Vector v(array); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(1.0 - v.get(3)) < TOLERANCE ); } { // 4th element defaults to 1 Vector v(3, 4, 5); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(1.0 - v.get(3)) < TOLERANCE ); } { Vector v(3, 4, 5, 6); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(3)) < TOLERANCE ); } } void testVectorSetGet() { { Vector v; QVERIFY_TRUE( fabs(v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(1 - v.get(3)) < TOLERANCE ); } { Vector v; v.set(0, 3); v.set(1, 4); v.set(2, 5); v.set(3, 6); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(3)) < TOLERANCE ); } { Vector v; v[0] = 3; v[1] = 4; v[2] = 5; v[3] = 6; QVERIFY_TRUE( fabs(3 - v[0]) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v[1]) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v[2]) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v[3]) < TOLERANCE ); } { const Vector v(3, 4, 5, 6); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(3)) < TOLERANCE ); } { const Vector v(3, 4, 5, 6); QVERIFY_TRUE( fabs(3 - v[0]) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v[1]) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v[2]) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v[3]) < TOLERANCE ); } } void testVectorGetVector() { { const Vector vec(3, 4, 5, 6); const double * v = vec.getVector(); QVERIFY_TRUE( fabs(3 - v[0]) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v[1]) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v[2]) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v[3]) < TOLERANCE ); } { Vector vec(3, 4, 5, 6); double * v = vec.getVector(); QVERIFY_TRUE( fabs(3 - v[0]) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v[1]) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v[2]) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v[3]) < TOLERANCE ); v[1] = 7.0; QVERIFY_TRUE( fabs(3 - vec[0]) < TOLERANCE ); QVERIFY_TRUE( fabs(7 - vec[1]) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - vec[2]) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - vec[3]) < TOLERANCE ); } } void testVectorSetAll() { { // 4th element defaults to 1 Vector v(3, 4, 5, 6); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(3)) < TOLERANCE ); v.setAll( 7, 8, 9 ); QVERIFY_TRUE( fabs(7 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(8 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(9 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(1 - v.get(3)) < TOLERANCE ); } { Vector v(3, 4, 5, 6); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(3)) < TOLERANCE ); v.setAll( 7, 8, 9, 10 ); QVERIFY_TRUE( fabs(7 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(8 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(9 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(10 - v.get(3)) < TOLERANCE ); } { // 4th element defaults to 1 Vector v(3, 4, 5, 6); QVERIFY_TRUE( fabs(3 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(5 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(3)) < TOLERANCE ); double v1[4] = { 7, 8, 9, 10 }; v.setAll( v1, 4 ); QVERIFY_TRUE( fabs(7 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(8 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(9 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(10 - v.get(3)) < TOLERANCE ); // Don't set v[3] double v2[4] = { 2, 4, 6, 8 }; v.setAll( v2, 3 ); QVERIFY_TRUE( fabs(2 - v.get(0)) < TOLERANCE ); QVERIFY_TRUE( fabs(4 - v.get(1)) < TOLERANCE ); QVERIFY_TRUE( fabs(6 - v.get(2)) < TOLERANCE ); QVERIFY_TRUE( fabs(10 - v.get(3)) < TOLERANCE ); } } void testVectorEqual() { { const Vector vec( 1, 2, 3, 4 ); const Vector exp( 1, 2, 3, 4 ); QVERIFY_TRUE(exp == vec); } { const Vector vec( 1, 2, 3, 4 ); const Vector exp( 1.001, 2, 3, 4 ); QVERIFY_FALSE(exp == vec); } { const Vector vec( 1, 2, 3, 4 ); const Vector exp( 1, 2.001, 3, 4 ); QVERIFY_FALSE(exp == vec); } { const Vector vec( 1, 2, 3, 4 ); const Vector exp( 1, 2, 3.001, 4 ); QVERIFY_FALSE(exp == vec); } { const Vector vec( 1, 2, 3, 4 ); const Vector exp( 1, 2, 3, 4.001 ); QVERIFY_FALSE(exp == vec); } } void testVectorScale() { { Vector v(3, 4, 5, 6); const Vector exp(-6, -8, -10, -12); v.scale( -2 ); QVERIFY_TRUE( v == exp ); } { Vector v(3, 4, 5, 6); const Vector exp(-6, -8, -10, 6); v.scale3( -2 ); QVERIFY_TRUE( v == exp ); } { const Vector v(3, 4, 5, 6); const Vector exp(-6, -8, -10, -12); const Vector res = v * -2.0; QVERIFY_TRUE( res == exp ); } } void testVectorPlusEquals() { { Vector lhs(1, 2, 3, 4); const Vector rhs(3, 4, 5, 6); const Vector exp(4, 6, 8, 10); lhs += rhs; QVERIFY_TRUE( lhs == exp ); } { Vector lhs(1, 2, 3, 4); const Vector exp(2, 4, 6, 8); lhs += lhs; QVERIFY_TRUE( lhs == exp ); } { const Vector lhs(1, 2, 3, 4); const Vector rhs(3, 4, 5, 6); const Vector exp(4, 6, 8, 10); const Vector res = lhs + rhs; QVERIFY_TRUE( res == exp ); } } void testVectorMinusEquals() { { Vector lhs(1, 2, 3, 4); const Vector rhs(2, 4, 6, 8); const Vector exp(-1, -2, -3, -4); lhs -= rhs; QVERIFY_TRUE( lhs == exp ); } { Vector lhs(1, 2, 3, 4); const Vector exp(0, 0, 0, 0); lhs -= lhs; QVERIFY_TRUE( lhs == exp ); } { const Vector lhs(1, 2, 3, 4); const Vector rhs(2, 4, 6, 8); const Vector exp(-1, -2, -3, -4); const Vector res = lhs - rhs; QVERIFY_TRUE( res == exp ); } } void testVectorMag() { { const Vector x(1, 0, 0, 1); const Vector y(0, 2, 0, 1); const Vector z(0, 0, 3, 1); QVERIFY_TRUE( fabs(x.mag3() - 1) < TOLERANCE ); QVERIFY_TRUE( fabs(y.mag3() - 2) < TOLERANCE ); QVERIFY_TRUE( fabs(z.mag3() - 3) < TOLERANCE ); } { const Vector x(1, 0, 0, 0); const Vector y(0, 2, 0, 0); const Vector z(0, 0, 3, 0); const Vector w(0, 0, 0, 4); QVERIFY_TRUE( fabs(x.mag() - 1) < TOLERANCE ); QVERIFY_TRUE( fabs(y.mag() - 2) < TOLERANCE ); QVERIFY_TRUE( fabs(z.mag() - 3) < TOLERANCE ); QVERIFY_TRUE( fabs(w.mag() - 4) < TOLERANCE ); } { const Vector v(1, -2, 3, -4); QVERIFY_TRUE( fabs(v.mag3() - 3.741657) < TOLERANCE ); QVERIFY_TRUE( fabs(v.mag() - 5.477226) < TOLERANCE ); } } void testVectorDot() { { const Vector lhs(1, 0, 0, 1); const Vector rhs(1, 0, 0, 1); QVERIFY_TRUE( fabs(lhs.dot3(rhs) - 1 ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); const Vector rhs(0, 1, 0, 1); QVERIFY_TRUE( fabs(lhs.dot3(rhs) ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); const Vector rhs(-1, 0, 0, 1); QVERIFY_TRUE( fabs(lhs.dot3(rhs) + 1 ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); const Vector rhs(0, -1, 0, 1); QVERIFY_TRUE( fabs(lhs.dot3(rhs) ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); Vector rhs(1, -1, 0, 1); rhs.normalize3(); QVERIFY_TRUE( fabs(lhs.dot3(rhs) - cos(PI / 4)) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); Vector rhs(-1, -1, 0, 1); rhs.normalize3(); QVERIFY_TRUE( fabs(lhs.dot3(rhs) - cos(PI * 3 / 4)) < TOLERANCE ); } } void testVectorNormalize() { { Vector vec(1, 2, 3, 4); Vector exp(0.267261, 0.534522, 0.801784, 4); vec.normalize3(); QVERIFY_TRUE( vec == exp ); } { Vector vec(1, 2, 3, 4); Vector exp(0.182574, 0.365148, 0.547723, 0.730297); vec.normalize(); QVERIFY_TRUE( vec == exp ); } } void testVectorCross() { // FIXME could be more thorough { const Vector lhs(1, 0, 0, 1); const Vector rhs(0, 1, 0, 1); const Vector exp(0, 0, 1, 1); const Vector res = lhs.cross3(rhs); QVERIFY_TRUE( res == exp ); } { const Vector lhs(0, 1, 0, 1); const Vector rhs(1, 0, 0, 1); const Vector exp(0, 0, -1, 1); const Vector res = lhs.cross3(rhs); QVERIFY_TRUE( res == exp ); } } void testVectorTranslate() { // FIXME this is not translation, this is transformation. // 1) Make this actually a translation. // 2) Change Vector::translate calls that should be transform // call Vector::transform instead. { Matrix mat; Vector vec(1, 1, 1); const Vector exp(5, 6, 7, 1); mat.setTranslation( 4, 5, 6 ); vec.translate( mat ); QVERIFY_TRUE( vec == exp ); } } void testVectorTransform() { // Translate { Matrix mat; Vector vec(1, 1, 1); Vector exp(5, 6, 7); mat.setTranslation( 4, 5, 6 ); vec.transform( mat ); QVERIFY_TRUE( vec == exp ); } // Translate (ignored, transform3) { Matrix mat; Vector vec(1, 1, 1); Vector exp(1, 1, 1); mat.setTranslation( 4, 5, 6 ); vec.transform3( mat ); QVERIFY_TRUE( vec == exp ); } // X Rotation { Matrix mat; Vector vec(1, 2, 3); Vector exp(1, -3, 2); mat.setRotation( Vector(PI / 2, 0, 0) ); vec.transform( mat ); QVERIFY_TRUE( vec == exp ); } // Y Rotation { Matrix mat; Vector vec(1, 2, 3); Vector exp(-3, 2, 1); mat.setRotation( Vector(0, -PI / 2, 0) ); vec.transform( mat ); QVERIFY_TRUE( vec == exp ); } // Z Rotation { Matrix mat; Vector vec(1, 2, 3); Vector exp(2, -1, 3); mat.setRotation( Vector(0, 0, PI*3 / 2) ); vec.transform( mat ); QVERIFY_TRUE( vec == exp ); } // Scale { Matrix mat; Vector vec(1, 2, 3); Vector exp(2, 6, 12, 5); mat.set( 0, 0, 2.0 ); mat.set( 1, 1, 3.0 ); mat.set( 2, 2, 4.0 ); mat.set( 3, 3, 5.0 ); vec.transform( mat ); QVERIFY_TRUE( vec == exp ); } // Scale3 { Matrix mat; Vector vec(1, 2, 3); Vector exp(2, 6, 12); mat.set( 0, 0, 2.0 ); mat.set( 1, 1, 3.0 ); mat.set( 2, 2, 4.0 ); mat.set( 3, 3, 5.0 ); vec.transform3( mat ); QVERIFY_TRUE( vec == exp ); } // Scale { Matrix mat; const Vector vec(1, 2, 3); const Vector exp(2, 6, 12, 5); mat.set( 0, 0, 2.0 ); mat.set( 1, 1, 3.0 ); mat.set( 2, 2, 4.0 ); mat.set( 3, 3, 5.0 ); const Vector res = vec * mat; QVERIFY_TRUE( res == exp ); } } void testMatrixInit() { const Matrix m; for ( int c = 0; c < 4; ++c ) { for ( int r = 0; r < 4; ++r ) { if ( r == c ) { QVERIFY_TRUE(float_equiv(m.get(r, c), 1.0)); } else { QVERIFY_TRUE(float_equiv(m.get(r, c), 0.0)); } } } } void testMatrixLoadIdentity() { Matrix m; QVERIFY_TRUE( m.isIdentity() ); for ( int c = 0; c < 4; ++c ) { for ( int r = 0; r < 4; ++r ) { m.set(r, c, 4.0); QVERIFY_TRUE(float_equiv(m.get(r, c), 4.0)); } } QVERIFY_FALSE( m.isIdentity() ); m.loadIdentity(); QVERIFY_TRUE( m.isIdentity() ); for ( int c = 0; c < 4; ++c ) { for ( int r = 0; r < 4; ++r ) { if ( r == c ) { QVERIFY_TRUE(float_equiv(m.get(r, c), 1.0)); } else { QVERIFY_TRUE(float_equiv(m.get(r, c), 0.0)); } } } } void testMatrixSetGet() { Matrix m; QVERIFY_TRUE( m.isIdentity() ); for ( int c = 0; c < 4; ++c ) { for ( int r = 0; r < 4; ++r ) { m.set(r, c, r * 8.0 + c); } } for ( int c = 0; c < 4; ++c ) { for ( int r = 0; r < 4; ++r ) { QVERIFY_TRUE(float_equiv(m.get(r, c), r * 8.0 + c)); } } } void testMatrixEqual() { for ( int c = 0; c < 4; ++c ) { for ( int r = 0; r < 4; ++r ) { Matrix lhs; Matrix rhs; QVERIFY_TRUE(lhs == rhs); QVERIFY_TRUE(lhs.equiv(rhs)); lhs.set(r, c, lhs.get(r, c) + 0.001); QVERIFY_FALSE(lhs == rhs); QVERIFY_FALSE(lhs.equiv(rhs)); } } } void testMatrixEquiv() { { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(-PI, 0, 0) ); rhs.setRotation( Vector(PI, 0, 0) ); QVERIFY_TRUE(lhs.equiv(rhs)); } { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(-PI / 2.0, 0, 0) ); rhs.setRotation( Vector(PI / 2.0, 0, 0) ); QVERIFY_FALSE(lhs.equiv(rhs)); } { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(0, -PI, 0) ); rhs.setRotation( Vector(0, PI, 0) ); QVERIFY_TRUE(lhs.equiv(rhs)); } { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(0, -PI / 2.0, 0) ); rhs.setRotation( Vector(0, PI / 2.0, 0) ); QVERIFY_FALSE(lhs.equiv(rhs)); } { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(0, 0, -PI) ); rhs.setRotation( Vector(0, 0, PI) ); QVERIFY_TRUE(lhs.equiv(rhs)); } { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(0, 0, -PI / 2.0) ); rhs.setRotation( Vector(0, 0, PI / 2.0) ); QVERIFY_FALSE(lhs.equiv(rhs)); } } void testMatrixSetGetTranslation() { { Matrix m; m.setTranslation( 1, 2, 3 ); double x = 0, y = 0, z = 0; m.getTranslation( x, y, z ); QVERIFY_TRUE(float_equiv(x, 1.0)); QVERIFY_TRUE(float_equiv(y, 2.0)); QVERIFY_TRUE(float_equiv(z, 3.0)); } { Matrix m; m.setTranslation( Vector(4, 6, 8) ); Vector v; m.getTranslation( v ); QVERIFY_TRUE(float_equiv(v[0], 4.0)); QVERIFY_TRUE(float_equiv(v[1], 6.0)); QVERIFY_TRUE(float_equiv(v[2], 8.0)); } { Matrix m; double array[3] = { 1, 4, 9 }; m.setTranslation( array ); double v[3] = { 0, 0, 0 }; m.getTranslation( v ); QVERIFY_TRUE(float_equiv(v[0], 1.0)); QVERIFY_TRUE(float_equiv(v[1], 4.0)); QVERIFY_TRUE(float_equiv(v[2], 9.0)); } } void testMatrixSetGetRotation() { { Matrix m; m.setRotation( Vector(PI/2, PI/3, PI/4) ); Vector v; m.getRotation( v ); QVERIFY_TRUE(float_equiv(v[0], PI/2)); QVERIFY_TRUE(float_equiv(v[1], PI/3)); QVERIFY_TRUE(float_equiv(v[2], PI/4)); double x = 0, y = 0, z = 0; m.getRotation( x, y, z ); QVERIFY_TRUE(float_equiv(x, PI/2)); QVERIFY_TRUE(float_equiv(y, PI/3)); QVERIFY_TRUE(float_equiv(z, PI/4)); } { Matrix m; double vec[3] = { PI/3, PI/4, PI/5 }; m.setRotation( vec ); double v[3] = { 0, 0, 0 }; m.getRotation( v ); QVERIFY_TRUE(float_equiv(v[0], PI/3)); QVERIFY_TRUE(float_equiv(v[1], PI/4)); QVERIFY_TRUE(float_equiv(v[2], PI/5)); } } void testMatrixApplyRotation() { { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); Vector vec(0, 2, 0); const Vector exp(0, 0, 2); m.apply( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); double vec[4] = { 0, 2, 0, 1 }; const Vector exp(0, 0, 2); m.apply( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); float vec[4] = { 0, 2, 0, 1 }; const Vector exp(0, 0, 2); m.apply( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } // Apply with a 0 'w' element in the vector { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); Vector vec(0, 2, 0, 0); const Vector exp(0, 0, 2, 0); m.apply( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); double vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 0, 2, 0); m.apply( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); float vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 0, 2, 0); m.apply( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } // Apply3 { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); Vector vec(0, 2, 0, 0); const Vector exp(0, 0, 2, 0); m.apply3( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); double vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 0, 2, 0); m.apply3( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); float vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 0, 2, 0); m.apply3( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } // Apply3x { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); Vector vec(0, 2, 0, 0); const Vector exp(0, 0, 2, 0); m.apply3x( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); double vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 0, 2); m.apply3x( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setRotation( Vector(PI/2, 0, 0) ); float vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 0, 2); m.apply3x( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } } void testMatrixApplyTranslation() { { Matrix m; m.setTranslation( Vector(1, 2, 3) ); Vector vec(0, 2, 0); const Vector exp(1, 4, 3); m.apply( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); double vec[4] = { 0, 2, 0, 1 }; const Vector exp(1, 4, 3); m.apply( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); float vec[4] = { 0, 2, 0, 1 }; const Vector exp(1, 4, 3); m.apply( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } // Apply with a 0 'w' element in the vector { Matrix m; m.setTranslation( Vector(1, 2, 3) ); Vector vec(0, 2, 0, 0); const Vector exp(0, 2, 0, 0); m.apply( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); double vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 2, 0, 0); m.apply( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); float vec[4] = { 0, 2, 0, 0 }; const Vector exp(0, 2, 0, 0); m.apply( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } // Apply3 { Matrix m; m.setTranslation( Vector(1, 2, 3) ); Vector vec(0, 2, 0); const Vector exp(0, 2, 0); m.apply3( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); double vec[4] = { 0, 2, 0, 1 }; const Vector exp(0, 2, 0); m.apply3( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); float vec[4] = { 0, 2, 0, 1 }; const Vector exp(0, 2, 0); m.apply3( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } // Apply3x { Matrix m; m.setTranslation( Vector(1, 2, 3, 0) ); Vector vec(0, 2, 0, 0); const Vector exp(1, 4, 3, 0); m.apply3x( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); double vec[4] = { 0, 2, 0, 0 }; const Vector exp(1, 4, 3); m.apply3x( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { Matrix m; m.setTranslation( Vector(1, 2, 3) ); float vec[4] = { 0, 2, 0, 0 }; const Vector exp(1, 4, 3); m.apply3x( vec ); QVERIFY_TRUE(float_equiv((float)exp[0], vec[0])); QVERIFY_TRUE(float_equiv((float)exp[1], vec[1])); QVERIFY_TRUE(float_equiv((float)exp[2], vec[2])); } } void testMatrixApplyScale() { { Matrix m; m.set(0, 0, 2); m.set(1, 1, -4); m.set(2, 2, 6); Vector vec(2, 2, 2); const Vector exp(4, -8, 12); m.apply( vec ); QVERIFY_TRUE(exp == vec); } // Apply with a 0 'w' element in the vector { Matrix m; m.set(0, 0, 2); m.set(1, 1, -4); m.set(2, 2, 6); Vector vec(2, 2, 2, 0); const Vector exp(4, -8, 12, 0); m.apply( vec ); QVERIFY_TRUE(exp == vec); } // Apply3 { Matrix m; m.set(0, 0, 2); m.set(1, 1, -4); m.set(2, 2, 6); Vector vec(2, 2, 2, 0); const Vector exp(4, -8, 12, 0); m.apply3( vec ); QVERIFY_TRUE(exp == vec); } // Apply3x { Matrix m; m.set(0, 0, 2); m.set(1, 1, -4); m.set(2, 2, 6); Vector vec(2, 2, 2, 0); const Vector exp(4, -8, 12, 0); m.apply3( vec ); QVERIFY_TRUE(exp == vec); } } void testMatrixApplyRotationTranslation() { Matrix m; m.setTranslation( Vector(1, 2, 3) ); m.setRotation( Vector(0, -PI/2, 0) ); { double vec[4] = { 2, 0, 0, 1 }; const Vector exp(1, 2, 5); m.apply( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { double vec[4] = { 2, 0, 0, 0 }; const Vector exp(0, 0, 2, 0); m.apply( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { double vec[4] = { 2, 0, 0, 0 }; const Vector exp(1, 2, 5, 0); m.apply3x( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { double vec[4] = { 2, 0, 0, 1 }; const Vector exp(0, 0, 2); m.apply3( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } } void testMatrixApplyInverse() { Matrix m; m.setTranslation( Vector(1, 2, 3) ); m.setRotation( Vector(0, -PI/2, 0) ); { // Inverse rotate doesn't apply translation double vec[4] = { 2, 0, 0, 1 }; const Vector exp(0, 0, -2); m.inverseRotateVector( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } { // Inverse translate doesn't apply rotation double vec[4] = { 2, 0, 0, 1 }; const Vector exp(1, -2, -3); m.inverseTranslateVector( vec ); QVERIFY_TRUE(float_equiv(exp[0], vec[0])); QVERIFY_TRUE(float_equiv(exp[1], vec[1])); QVERIFY_TRUE(float_equiv(exp[2], vec[2])); } } void testMatrixSetInverseRotation() { Matrix m; m.setInverseRotation( Vector(0, -PI/2, 0).getVector() ); Vector vec(2, 0, 0); Vector exp(0, 0, -2); m.apply( vec ); QVERIFY_TRUE(exp == vec); } void testMatrixSetInverseInDegrees() { Matrix m; m.setInverseRotationInDegrees( Vector(0, -90, 0).getVector() ); Vector vec(2, 0, 0); Vector exp(0, 0, -2); m.apply( vec ); QVERIFY_TRUE(exp == vec); } void testMatrixSetRotationInDegrees() { { Matrix m; m.setRotationInDegrees( Vector(0, 90, 0) ); Vector vec(2, 0, 0); Vector exp(0, 0, -2); m.apply( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; double rot[3] = { 0, 90, 0 }; m.setRotationInDegrees( rot ); Vector vec(2, 0, 0); Vector exp(0, 0, -2); m.apply( vec ); QVERIFY_TRUE(exp == vec); } { Matrix m; double rot[3] = { 0, 90, 0 }; m.setRotationInDegrees( rot[0], rot[1], rot[2] ); Vector vec(2, 0, 0); Vector exp(0, 0, -2); m.apply( vec ); QVERIFY_TRUE(exp == vec); } } void testMatrixSetGetQuaternion() { { Matrix m; m.setRotationOnAxis( Vector(1, 0, 0).getVector(), PI/2 ); Vector exp(PI/2, 0, 0); Vector rot(0, 0, 0); m.getRotation(rot.getVector()); QVERIFY_TRUE(exp == rot); Quaternion qexp; qexp.setRotationOnAxis(1, 0, 0, PI/2); Quaternion quat; m.getRotationQuaternion(quat); QVERIFY_TRUE(qexp == quat); } { Matrix m; Quaternion q; q.setRotationOnAxis( 0, 1, 0, PI/2 ); m.setRotationQuaternion( q ); Vector exp(0, PI/2, 0); Vector rot(0, 0, 0); m.getRotation(rot.getVector()); QVERIFY_TRUE(exp == rot); Quaternion qexp; qexp.setRotationOnAxis(0, 1, 0, PI/2); Quaternion quat; m.getRotationQuaternion(quat); QVERIFY_TRUE(qexp == quat); } } void testMatrixQuaternionRotation() { Vector v(1, 0, 1); v.normalize3(); Matrix m; m.setRotationOnAxis( v.getVector(), PI/2 ); Vector v1(2, 0, 0); Vector v2(0, 0, 2); Vector exp1(1, sqrt(2), 1); Vector exp2(1, -sqrt(2), 1); m.apply(v1); m.apply(v2); QVERIFY_TRUE( exp1 == v1 ); QVERIFY_TRUE( exp2 == v2 ); } void testMatrixMultiply() { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(PI/2, 0.0, 0.0) ); rhs.setRotation( Vector(0.0, PI/2, 0.0) ); { // 0 0 -1 0 // 1 0 0 0 // 0 -1 0 0 // 0 0 0 1 Matrix exp; exp.set(0, 0, 0); exp.set(1, 1, 0); exp.set(2, 2, 0); exp.set(0, 2, -1); exp.set(1, 0, 1); exp.set(2, 1, -1); Matrix res = lhs * rhs; QVERIFY_TRUE(exp == res); } { // 0 1 0 0 // 0 0 1 0 // 1 0 0 0 // 0 0 0 1 Matrix exp; exp.set(0, 0, 0); exp.set(1, 1, 0); exp.set(2, 2, 0); exp.set(0, 1, 1); exp.set(1, 2, 1); exp.set(2, 0, 1); Matrix res = rhs * lhs; QVERIFY_TRUE(exp == res); } { // post multiply: lhs = rhs * lhs // 0 1 0 0 // 0 0 1 0 // 1 0 0 0 // 0 0 0 1 Matrix exp; exp.set(0, 0, 0); exp.set(1, 1, 0); exp.set(2, 2, 0); exp.set(0, 1, 1); exp.set(1, 2, 1); exp.set(2, 0, 1); Matrix res = lhs; res.postMultiply(rhs); QVERIFY_TRUE(exp == res); } } void testMatrixNormalizeRotation() { Matrix lhs; Matrix rhs; lhs.setRotation( Vector(PI/2, PI, -PI/2) ); rhs.set( 0, 0, 2 ); rhs.set( 1, 1, 3 ); rhs.set( 2, 2, 4 ); Matrix m = rhs * lhs; { Vector vec( 1, 2, 3 ); m.apply( vec ); Vector exp(-12, 2, -6); QVERIFY_TRUE( exp == vec ); } m.normalizeRotation(); { Vector vec( 1, 2, 3 ); m.apply( vec ); Vector exp( -3, 1, -2 ); QVERIFY_TRUE( exp == vec ); } } void testMatrixGetInverse() { { Matrix m; m.setRotation( Vector( PI/2, 0, 0 ) ); Matrix inv = m.getInverse(); Matrix id = m * inv; QVERIFY_TRUE( id.isIdentity() ); QVERIFY_TRUE( fabs(m.getDeterminant()) > TOLERANCE ); } { Matrix m; m.setTranslation( 1, 2, 3 ); Matrix inv = m.getInverse(); Matrix id = m * inv; QVERIFY_TRUE( id.isIdentity() ); QVERIFY_TRUE( fabs(m.getDeterminant()) > TOLERANCE ); } { Matrix m; m.set( 0, 0, 2 ); m.set( 1, 1, 3 ); m.set( 2, 2, 4 ); Matrix inv = m.getInverse(); Matrix id = m * inv; QVERIFY_TRUE( id.isIdentity() ); QVERIFY_TRUE( fabs(m.getDeterminant()) > TOLERANCE ); } { // Can't invert Matrix m; m.set( 0, 0, 2 ); m.set( 1, 1, 0 ); m.set( 2, 2, 4 ); Matrix inv = m.getInverse(); Matrix id = m * inv; QVERIFY_FALSE( id.isIdentity() ); QVERIFY_FALSE( fabs(m.getDeterminant()) > TOLERANCE ); } } void testQuaternionInit() { { const Quaternion q; const Vector exp( 0, 0, 0, 1 ); QVERIFY_TRUE( exp == q ); } { const Quaternion q( Vector(1, 2, 3, 4).getVector() ); const Vector exp( 1, 2, 3, 4 ); QVERIFY_TRUE( exp == q ); } { const Quaternion q( Vector(1, 2, 3, 4) ); const Vector exp( 1, 2, 3, 4 ); QVERIFY_TRUE( exp == q ); } } void testQuaternionSet() { Quaternion q; { const Vector exp( 0, 0, 0, 1 ); QVERIFY_TRUE( exp == q ); } q.set(0, 1); q.set(1, 2); q.set(2, 3); q.set(3, 4); { const Vector exp( 1, 2, 3, 4 ); QVERIFY_TRUE( exp == q ); } QVERIFY_TRUE( float_equiv( 1.0, q.get(0) ) ); QVERIFY_TRUE( float_equiv( 2.0, q.get(1) ) ); QVERIFY_TRUE( float_equiv( 3.0, q.get(2) ) ); QVERIFY_TRUE( float_equiv( 4.0, q.get(3) ) ); const double * vec = q.getVector(); QVERIFY_TRUE( float_equiv( 1.0, vec[0] ) ); QVERIFY_TRUE( float_equiv( 2.0, vec[1] ) ); QVERIFY_TRUE( float_equiv( 3.0, vec[2] ) ); QVERIFY_TRUE( float_equiv( 4.0, vec[3] ) ); } // Note this function also tests Quaternion::getRotationOnAxis // Quaternion::setRotationOnAxis is tested in the Matrix code void testQuaternionEulerAngles() { { Quaternion q; q.setEulerAngles( Vector(PI/2, 0, 0).getVector() ); double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 1, 0, 0 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } { Quaternion q; q.setEulerAngles( Vector(0, PI/2, 0).getVector() ); double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 0, 1, 0 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } { Quaternion q; q.setEulerAngles( Vector(0, 0, PI/2).getVector() ); double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 0, 0, 1 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } } void testQuaternionSwapHandedness() { Quaternion q; q.setEulerAngles( Vector(PI/2, 0, 0).getVector() ); { double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 1, 0, 0 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } q.swapHandedness(); { double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 1, 0, 0 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } } void testQuaternionNormalize() { Quaternion q(Vector(1, 2, 3, 4).getVector()); q.normalize(); Vector exp(1, 2, 3, 4); exp.normalize(); QVERIFY_TRUE( exp == q ); } void testQuaternionSetRotationToPoint() { Vector face(0, 0, 2); Vector point(3, 0, 0); { Quaternion q; q.setRotationToPoint( face, point ); double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 0, 1, 0 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } { Quaternion q; q.setRotationToPoint( face[0], face[1], face[2], point[0], point[1], point[2] ); double axis[3] = { 0, 0, 0 }; double rad = 0; q.getRotationOnAxis( axis, rad ); double exp[3] = { 0, 1, 0 }; QVERIFY_TRUE( floatCompareVector(axis, exp, 3) ); QVERIFY_TRUE( float_equiv(rad, PI/2) ); } } void testDistance() { // vector functions { Vector p1(2, 3, 4); Vector p2(4, 6, 8); QVERIFY_TRUE( float_equiv(5.385165, distance(p1, p2)) ); } { Vector p1(2, 3, 4); Vector p2(4, 6, 8); QVERIFY_TRUE( float_equiv(5.385165, distance(p1.getVector(), p2.getVector())) ); } // template functions { Vector p1(2, 3, 4); Vector p2(4, 6, 8); QVERIFY_TRUE( float_equiv(5.385165, distance( p1[0], p1[1], p1[2], p2[0], p2[1], p2[2] )) ); } { Vector p1(2, 3, 4); Vector p2(4, 6, 8); QVERIFY_TRUE( float_equiv(3.605551, distance( p1[0], p1[1], p2[0], p2[1] )) ); } } void testMag() { Vector vec(2, 3, 4); QVERIFY_TRUE( float_equiv(5.385165, mag3(vec.getVector())) ); } void testNormalize() { Vector vec(2, 3, 4, 5); normalize3( vec.getVector() ); Vector exp(0.371391, 0.557086, 0.742781, 5); QVERIFY_TRUE(exp == vec); } void testDot() { { const Vector lhs(1, 0, 0, 1); const Vector rhs(1, 0, 0, 1); QVERIFY_TRUE( fabs(dot3(lhs.getVector(), rhs.getVector()) - 1 ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); const Vector rhs(0, 1, 0, 1); QVERIFY_TRUE( fabs(lhs.dot3(rhs) ) < TOLERANCE ); QVERIFY_TRUE( fabs(dot3(lhs.getVector(), rhs.getVector()) ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); const Vector rhs(-1, 0, 0, 1); QVERIFY_TRUE( fabs(dot3(lhs.getVector(), rhs.getVector()) + 1 ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); const Vector rhs(0, -1, 0, 1); QVERIFY_TRUE( fabs(dot3(lhs.getVector(), rhs.getVector()) ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); Vector rhs(1, -1, 0, 1); rhs.normalize3(); QVERIFY_TRUE( fabs(dot3(lhs.getVector(), rhs.getVector()) - cos(PI / 4) ) < TOLERANCE ); } { const Vector lhs(1, 0, 0, 1); Vector rhs(-1, -1, 0, 1); rhs.normalize3(); QVERIFY_TRUE( fabs(dot3(lhs.getVector(), rhs.getVector()) - cos(PI * 3 / 4) ) < TOLERANCE ); } } void testEquiv() { { const Vector v1( 1, 1, 1 ); const Vector v2( 1, 1, 1 ); QVERIFY_TRUE( equiv3( v1.getVector(), v2.getVector() ) ); } { const Vector v1( 1, 1, 1 ); const Vector v2( 1.001, 1, 1 ); QVERIFY_FALSE( equiv3( v1.getVector(), v2.getVector() ) ); } { const Vector v1( 1, 1, 1 ); const Vector v2( 1, 1.001, 1 ); QVERIFY_FALSE( equiv3( v1.getVector(), v2.getVector() ) ); } { const Vector v1( 1, 1, 1 ); const Vector v2( 1, 1, 1.001 ); QVERIFY_FALSE( equiv3( v1.getVector(), v2.getVector() ) ); } } void testCalculateNormal() { // FIXME could be more thorough { const Vector p1(1, 1, 1, 1); const Vector p2(3, 1, 1, 1); const Vector p3(1, 4, 1, 1); const Vector exp(0, 0, 1, 1); Vector normal; calculate_normal( normal.getVector(), p1.getVector(), p2.getVector(), p3.getVector() ); QVERIFY_TRUE( normal == exp ); } { const Vector p1(1, 1, 1, 1); const Vector p2(1, 4, 1, 1); const Vector p3(3, 1, 1, 1); const Vector exp(0, 0, -1, 1); Vector normal; calculate_normal( normal.getVector(), p1.getVector(), p2.getVector(), p3.getVector() ); QVERIFY_TRUE( normal == exp ); } { const Vector lhs(0, 1, 0, 1); const Vector rhs(1, 0, 0, 1); const Vector exp(0, 0, -1, 1); const Vector res = lhs.cross3(rhs); QVERIFY_TRUE( res == exp ); } } // Doesn't actually test anything, but it's just for debugging anyway. void testShow() { Matrix m; m.show(); Vector v; v.show(); Quaternion q; q.show(); } }; QTEST_MAIN(GlmathTest) #include "glmath_test.moc" mm3d-1.3.15/src/tests/libmm3d/intfile.cc000066400000000000000000000013651466047437300177050ustar00rootroot00000000000000#include #include #include #include #include int main( int argc, char * argv[] ) { if ( argc != 3 ) { fprintf( stderr, "Usage: ./intfile filename dword_count\n" ); return -1; } const char * filename = argv[1]; size_t dword_count = atoi( argv[2] ); FILE * fp = fopen( filename, "w" ); if ( !fp ) { fprintf( stderr, "open %s: %s\n", filename, strerror(errno) ); return -1; } for ( size_t t = 0; t < dword_count; ++t ) { uint32_t val = t; if ( fwrite( &val, sizeof(val), 1, fp ) < 1 ) { fprintf( stderr, "write %s: %s\n", filename, strerror(errno) ); return -1; } } fclose( fp ); return 0; } mm3d-1.3.15/src/tests/libmm3d/little_endian_test.cc000066400000000000000000000135571466047437300221330ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests libmm3d/endianconfig.h as little endian #include #include "test_common.h" #include "config.h" #undef BYTEORDER #define BYTEORDER 1234 #include "endianconfig.h" class BigEndianTest : public QObject { Q_OBJECT private slots: void testLittle32() { uint8_t in[4] = { 0x01, 0x02, 0x03, 0x04 }; // little to host { uint32_t source = 0; memcpy( &source, in, sizeof(source) ); uint32_t host = ltoh_u32( source ); QVERIFY_EQ( 0, memcmp( &host, in, sizeof(host) ) ); } { int32_t source = 0; memcpy( &source, in, sizeof(source) ); int32_t host = ltoh_32( source ); QVERIFY_EQ( 0, memcmp( &host, in, sizeof(host) ) ); } // host to little { uint32_t host = 0; memcpy( &host, in, sizeof(host) ); uint32_t dest = htol_u32( host ); QVERIFY_EQ( 0, memcmp( &dest, in, sizeof(dest) ) ); } { int32_t host = 0; memcpy( &host, in, sizeof(host) ); int32_t dest = htol_32( host ); QVERIFY_EQ( 0, memcmp( &dest, in, sizeof(dest) ) ); } } void testLittle16() { uint8_t in[2] = { 0x01, 0x02 }; // little to host { uint16_t source = 0; memcpy( &source, in, sizeof(source) ); uint16_t host = ltoh_u16( source ); QVERIFY_EQ( 0, memcmp( &host, in, sizeof(host) ) ); } { int16_t source = 0; memcpy( &source, in, sizeof(source) ); int16_t host = ltoh_16( source ); QVERIFY_EQ( 0, memcmp( &host, in, sizeof(host) ) ); } // host to little { uint16_t host = 0; memcpy( &host, in, sizeof(host) ); uint16_t dest = htol_u16( host ); QVERIFY_EQ( 0, memcmp( &dest, in, sizeof(dest) ) ); } { int16_t host = 0; memcpy( &host, in, sizeof(host) ); int16_t dest = htol_16( host ); QVERIFY_EQ( 0, memcmp( &dest, in, sizeof(dest) ) ); } } void testLittleFloat() { uint8_t in[4] = { 0x01, 0x02, 0x03, 0x04 }; // little to host { float source = 0; memcpy( &source, in, sizeof(source) ); float host = ltoh_float( source ); QVERIFY_EQ( 0, memcmp( &host, in, sizeof(host) ) ); } // host to little { float host = 0; memcpy( &host, in, sizeof(host) ); float dest = htol_float( host ); QVERIFY_EQ( 0, memcmp( &dest, in, sizeof(dest) ) ); } } void testBig32() { uint8_t in[4] = { 0x01, 0x02, 0x03, 0x04 }; uint8_t out[4] = { 0x04, 0x03, 0x02, 0x01 }; // big to host { uint32_t source = 0; memcpy( &source, in, sizeof(source) ); uint32_t host = btoh_u32( source ); QVERIFY_EQ( 0, memcmp( &host, out, sizeof(host) ) ); } { int32_t source = 0; memcpy( &source, in, sizeof(source) ); int32_t host = btoh_32( source ); QVERIFY_EQ( 0, memcmp( &host, out, sizeof(host) ) ); } // host to big { uint32_t host = 0; memcpy( &host, in, sizeof(host) ); uint32_t dest = htob_u32( host ); QVERIFY_EQ( 0, memcmp( &dest, out, sizeof(dest) ) ); } { int32_t host = 0; memcpy( &host, in, sizeof(host) ); int32_t dest = htob_32( host ); QVERIFY_EQ( 0, memcmp( &dest, out, sizeof(dest) ) ); } } void testBig16() { uint8_t in[2] = { 0x01, 0x02 }; uint8_t out[2] = { 0x02, 0x01 }; // big to host { uint16_t source = 0; memcpy( &source, in, sizeof(source) ); uint16_t host = btoh_u16( source ); QVERIFY_EQ( 0, memcmp( &host, out, sizeof(host) ) ); } { int16_t source = 0; memcpy( &source, in, sizeof(source) ); int16_t host = btoh_16( source ); QVERIFY_EQ( 0, memcmp( &host, out, sizeof(host) ) ); } // host to big { uint16_t host = 0; memcpy( &host, in, sizeof(host) ); uint16_t dest = htob_u16( host ); QVERIFY_EQ( 0, memcmp( &dest, out, sizeof(dest) ) ); } { int16_t host = 0; memcpy( &host, in, sizeof(host) ); int16_t dest = htob_16( host ); QVERIFY_EQ( 0, memcmp( &dest, out, sizeof(dest) ) ); } } void testBigFloat() { uint8_t in[4] = { 0x01, 0x02, 0x03, 0x04 }; uint8_t out[4] = { 0x04, 0x03, 0x02, 0x01 }; // big to host { float source = 0; memcpy( &source, in, sizeof(source) ); float host = btoh_float( source ); QVERIFY_EQ( 0, memcmp( &host, out, sizeof(host) ) ); } // host to big { float host = 0; memcpy( &host, in, sizeof(host) ); float dest = htob_float( host ); QVERIFY_EQ( 0, memcmp( &dest, out, sizeof(dest) ) ); } } }; QTEST_MAIN(BigEndianTest) #include "big_endian_test.moc" mm3d-1.3.15/src/tests/libmm3d/memutil_test.cc000066400000000000000000000247751466047437300210000ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests local_ptr.h, local_array.h, release_ptr.h, // and file_closer.h #include #include #include #include #include "test_common.h" #include "local_ptr.h" #include "local_array.h" #include "release_ptr.h" #include "file_closer.h" class CountObject { public: CountObject( int val = 0 ) : m_val(val) { ++s_count; } ~CountObject() { --s_count; } static int s_count; int m_val; }; /* static */ int CountObject::s_count = 0; class ReleaseObject { public: ReleaseObject( int val = 0 ) : m_val(val) { ++s_count; } void release() const { delete this; } static int s_count; int m_val; private: ~ReleaseObject() { --s_count; } }; /* static */ int ReleaseObject::s_count = 0; class MemUtilTest : public QObject { Q_OBJECT private slots: void testLocalPtr() { QVERIFY_EQ( 0, CountObject::s_count ); // Mutable { local_ptr< CountObject > ptr( new CountObject(1) ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 1, CountObject::s_count ); CountObject * obj = new CountObject(2); QVERIFY_EQ( 2, CountObject::s_count ); QVERIFY_EQ( 1, ptr->m_val ); QVERIFY_EQ( 1, (*ptr).m_val ); CountObject * temp = ptr = obj; QVERIFY_EQ( 1, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 2, ptr->m_val ); QVERIFY_EQ( 2, temp->m_val ); temp = NULL; ptr.reset( NULL ); QVERIFY_EQ( 0, CountObject::s_count ); QVERIFY_TRUE( ptr.isnull() ); QVERIFY_TRUE( !ptr ); QVERIFY_TRUE( NULL == ptr.get() ); ptr = new CountObject(3); QVERIFY_EQ( 1, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 3, ptr->m_val ); ptr->m_val = 4; QVERIFY_EQ( 4, ptr->m_val ); } QVERIFY_EQ( 0, CountObject::s_count ); // Mutable, const object { local_ptr< const CountObject > ptr = new CountObject(1); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 1, CountObject::s_count ); CountObject * obj = new CountObject(2); QVERIFY_EQ( 2, CountObject::s_count ); QVERIFY_EQ( 1, ptr->m_val ); QVERIFY_EQ( 1, (*ptr).m_val ); const CountObject * temp = ptr = obj; QVERIFY_EQ( 1, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 2, ptr->m_val ); QVERIFY_EQ( 2, temp->m_val ); temp = NULL; ptr.reset( NULL ); QVERIFY_EQ( 0, CountObject::s_count ); QVERIFY_TRUE( ptr.isnull() ); QVERIFY_TRUE( !ptr ); QVERIFY_TRUE( NULL == ptr.get() ); ptr = new CountObject(3); QVERIFY_EQ( 1, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 3, ptr->m_val ); } QVERIFY_EQ( 0, CountObject::s_count ); // Const { const local_ptr< CountObject > ptr = new CountObject(1); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 1, CountObject::s_count ); QVERIFY_EQ( 1, ptr->m_val ); QVERIFY_EQ( 1, (*ptr).m_val ); } QVERIFY_EQ( 0, CountObject::s_count ); } void testLocalArray() { QVERIFY_EQ( 0, CountObject::s_count ); // Mutable { local_array< CountObject > ptr( new CountObject[10] ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 10, CountObject::s_count ); CountObject * obj = new CountObject[20]; obj[0].m_val = 7; QVERIFY_EQ( 30, CountObject::s_count ); QVERIFY_EQ( 0, ptr.get()[0].m_val ); CountObject * temp = ptr = obj; QVERIFY_EQ( 20, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 7, ptr.get()[0].m_val ); QVERIFY_EQ( 7, temp[0].m_val ); temp = NULL; ptr.reset( NULL ); QVERIFY_EQ( 0, CountObject::s_count ); QVERIFY_TRUE( ptr.isnull() ); QVERIFY_TRUE( !ptr ); QVERIFY_TRUE( NULL == ptr.get() ); ptr = new CountObject[5]; QVERIFY_EQ( 5, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); } QVERIFY_EQ( 0, CountObject::s_count ); // Mutable, const object { local_array< CountObject > ptr( new CountObject[10] ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 10, CountObject::s_count ); CountObject * obj = new CountObject[20]; obj[0].m_val = 7; QVERIFY_EQ( 30, CountObject::s_count ); QVERIFY_EQ( 0, ptr.get()[0].m_val ); const CountObject * temp = ptr = obj; QVERIFY_EQ( 20, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 7, ptr.get()[0].m_val ); QVERIFY_EQ( 7, temp[0].m_val ); temp = NULL; ptr.reset( NULL ); QVERIFY_EQ( 0, CountObject::s_count ); QVERIFY_TRUE( ptr.isnull() ); QVERIFY_TRUE( !ptr ); QVERIFY_TRUE( NULL == ptr.get() ); ptr = new CountObject[5]; QVERIFY_EQ( 5, CountObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); } QVERIFY_EQ( 0, CountObject::s_count ); // Const { const local_array< CountObject > ptr( new CountObject[10] ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 10, CountObject::s_count ); QVERIFY_EQ( 0, ptr.get()[0].m_val ); } QVERIFY_EQ( 0, CountObject::s_count ); } void testReleasePtr() { QVERIFY_EQ( 0, ReleaseObject::s_count ); // Mutable { release_ptr< ReleaseObject > ptr( new ReleaseObject(1) ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 1, ReleaseObject::s_count ); ReleaseObject * obj = new ReleaseObject(2); QVERIFY_EQ( 2, ReleaseObject::s_count ); QVERIFY_EQ( 1, ptr->m_val ); QVERIFY_EQ( 1, (*ptr).m_val ); ReleaseObject * temp = ptr = obj; QVERIFY_EQ( 1, ReleaseObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 2, ptr->m_val ); QVERIFY_EQ( 2, temp->m_val ); temp = NULL; ptr.reset( NULL ); QVERIFY_EQ( 0, ReleaseObject::s_count ); QVERIFY_TRUE( ptr.isnull() ); QVERIFY_TRUE( !ptr ); QVERIFY_TRUE( NULL == ptr.get() ); ptr = new ReleaseObject(3); QVERIFY_EQ( 1, ReleaseObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 3, ptr->m_val ); ptr->m_val = 4; QVERIFY_EQ( 4, ptr->m_val ); } QVERIFY_EQ( 0, ReleaseObject::s_count ); // Mutable, const object { release_ptr< const ReleaseObject > ptr = new ReleaseObject(1); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 1, ReleaseObject::s_count ); ReleaseObject * obj = new ReleaseObject(2); QVERIFY_EQ( 2, ReleaseObject::s_count ); QVERIFY_EQ( 1, ptr->m_val ); QVERIFY_EQ( 1, (*ptr).m_val ); const ReleaseObject * temp = ptr = obj; QVERIFY_EQ( 1, ReleaseObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 2, ptr->m_val ); QVERIFY_EQ( 2, temp->m_val ); temp = NULL; ptr.reset( NULL ); QVERIFY_EQ( 0, ReleaseObject::s_count ); QVERIFY_TRUE( ptr.isnull() ); QVERIFY_TRUE( !ptr ); QVERIFY_TRUE( NULL == ptr.get() ); ptr = new ReleaseObject(3); QVERIFY_EQ( 1, ReleaseObject::s_count ); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 3, ptr->m_val ); } QVERIFY_EQ( 0, ReleaseObject::s_count ); // Const { const release_ptr< ReleaseObject > ptr = new ReleaseObject(1); QVERIFY_FALSE( ptr.isnull() ); QVERIFY_FALSE( !ptr ); QVERIFY_EQ( 1, ReleaseObject::s_count ); QVERIFY_EQ( 1, ptr->m_val ); QVERIFY_EQ( 1, (*ptr).m_val ); } QVERIFY_EQ( 0, ReleaseObject::s_count ); } void testFileCloser() { QVERIFY_EQ( 0, CountObject::s_count ); int fd; // Use lseek to tell if the file descriptor associated with the // stream is invalid (stream closed). { FILE * fp = fopen( "memutil_test.cc", "r" ); QVERIFY_TRUE( fp != NULL ); if ( !fp ) return; fd = fileno( fp ); file_closer fc( fp ); QVERIFY_EQ( 0, (int) lseek(fd, 0, SEEK_SET ) ); FILE * fp2 = fopen( "memutil_test.cc", "r" ); QVERIFY_TRUE( fp2 != NULL ); if ( !fp2 ) return; // This should close the original FILE* fc = fp2; QVERIFY_NE( 0, (int) lseek(fd, 0, SEEK_SET ) ); QVERIFY_EQ( EBADF, errno ); fd = fileno( fp2 ); QVERIFY_EQ( 0, (int) lseek(fd, 0, SEEK_SET ) ); // Going out of scope will close the second FILE* } QVERIFY_NE( 0, (int) lseek(fd, 0, SEEK_SET ) ); QVERIFY_EQ( EBADF, errno ); } }; QTEST_MAIN(MemUtilTest) #include "memutil_test.moc" mm3d-1.3.15/src/tests/libmm3d/misc_test.cc000066400000000000000000000423741466047437300202520ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests misc.h #include #include #include #include #include #include "misc.h" #include "log.h" #include "test_common.h" class MiscTest : public QObject { Q_OBJECT private slots: void initTestCase() { log_enable_debug( false ); } void testPathIsAbsolute() { // Win32 QVERIFY_TRUE( pathIsAbsolute( "c:\\windows\\system32" ) ); QVERIFY_TRUE( pathIsAbsolute( "c:\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "a:\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "z:\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "A:\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "C:\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "Z:\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "\\" ) ); QVERIFY_TRUE( pathIsAbsolute( "\\windows\\system32" ) ); QVERIFY_FALSE( pathIsAbsolute( "windows\\system32" ) ); // Unix QVERIFY_TRUE( pathIsAbsolute( "/" ) ); QVERIFY_TRUE( pathIsAbsolute( "/etc/hosts" ) ); QVERIFY_FALSE( pathIsAbsolute( "etc/hosts" ) ); QVERIFY_FALSE( pathIsAbsolute( "" ) ); QVERIFY_FALSE( pathIsAbsolute( "." ) ); QVERIFY_FALSE( pathIsAbsolute( "./" ) ); } void testGetFilePath() { QVERIFY_EQ( std::string("/"), getFilePathFromPath( "/" ) ); QVERIFY_EQ( std::string("/"), getFilePathFromPath( "/etc" ) ); QVERIFY_EQ( std::string("/etc"), getFilePathFromPath( "/etc/hosts" ) ); QVERIFY_EQ( std::string("/home/user/.mm3d"), getFilePathFromPath( "/home/user/.mm3d/mm3drc" ) ); QVERIFY_EQ( std::string(".mm3d"), getFilePathFromPath( ".mm3d/mm3drc" ) ); QVERIFY_EQ( std::string("some/longer/dir"), getFilePathFromPath( "some/longer/dir/file" ) ); QVERIFY_EQ( std::string("."), getFilePathFromPath( "./file.txt" ) ); QVERIFY_EQ( std::string("."), getFilePathFromPath( "file.txt" ) ); QVERIFY_EQ( std::string("."), getFilePathFromPath( "./" ) ); QVERIFY_EQ( std::string("some/dir"), getFilePathFromPath( "some/dir/" ) ); QVERIFY_EQ( std::string("\\"), getFilePathFromPath( "\\" ) ); QVERIFY_EQ( std::string("\\"), getFilePathFromPath( "\\etc" ) ); QVERIFY_EQ( std::string("\\etc"), getFilePathFromPath( "\\etc\\hosts" ) ); QVERIFY_EQ( std::string("\\home\\user\\.mm3d"), getFilePathFromPath( "\\home\\user\\.mm3d\\mm3drc" ) ); QVERIFY_EQ( std::string(".mm3d"), getFilePathFromPath( ".mm3d\\mm3drc" ) ); QVERIFY_EQ( std::string("some\\longer\\dir"), getFilePathFromPath( "some\\longer\\dir\\file" ) ); QVERIFY_EQ( std::string("."), getFilePathFromPath( ".\\file.txt" ) ); QVERIFY_EQ( std::string("."), getFilePathFromPath( "file.txt" ) ); QVERIFY_EQ( std::string("."), getFilePathFromPath( ".\\" ) ); QVERIFY_EQ( std::string("some\\dir"), getFilePathFromPath( "some\\dir\\" ) ); } void testGetFileName() { QVERIFY_EQ( std::string(""), getFileNameFromPath( "/" ) ); QVERIFY_EQ( std::string("etc"), getFileNameFromPath( "/etc" ) ); QVERIFY_EQ( std::string("hosts"), getFileNameFromPath( "/etc/hosts" ) ); QVERIFY_EQ( std::string("mm3drc"), getFileNameFromPath( "/home/user/.mm3d/mm3drc" ) ); QVERIFY_EQ( std::string("mm3drc"), getFileNameFromPath( ".mm3d/mm3drc" ) ); QVERIFY_EQ( std::string("file"), getFileNameFromPath( "some/longer/dir/file" ) ); QVERIFY_EQ( std::string("file.txt"), getFileNameFromPath( "./file.txt" ) ); QVERIFY_EQ( std::string("file.txt"), getFileNameFromPath( "file.txt" ) ); QVERIFY_EQ( std::string(""), getFileNameFromPath( "./" ) ); QVERIFY_EQ( std::string(""), getFileNameFromPath( "some/dir/" ) ); QVERIFY_EQ( std::string(""), getFileNameFromPath( "\\" ) ); QVERIFY_EQ( std::string("etc"), getFileNameFromPath( "\\etc" ) ); QVERIFY_EQ( std::string("hosts"), getFileNameFromPath( "\\etc\\hosts" ) ); QVERIFY_EQ( std::string("mm3drc"), getFileNameFromPath( "\\home\\user\\.mm3d\\mm3drc" ) ); QVERIFY_EQ( std::string("mm3drc"), getFileNameFromPath( ".mm3d\\mm3drc" ) ); QVERIFY_EQ( std::string("file"), getFileNameFromPath( "some\\longer\\dir\\file" ) ); QVERIFY_EQ( std::string("file.txt"), getFileNameFromPath( ".\\file.txt" ) ); QVERIFY_EQ( std::string("file.txt"), getFileNameFromPath( "file.txt" ) ); QVERIFY_EQ( std::string(""), getFileNameFromPath( ".\\" ) ); QVERIFY_EQ( std::string(""), getFileNameFromPath( "some\\dir\\" ) ); } void testRemoveExtension() { QVERIFY_EQ( std::string("file"), removeExtension("file.txt") ); QVERIFY_EQ( std::string("a"), removeExtension("a.txt") ); QVERIFY_EQ( std::string("/etc/file"), removeExtension("/etc/file.txt") ); QVERIFY_EQ( std::string("file"), removeExtension("file") ); QVERIFY_EQ( std::string("/etc/file"), removeExtension("/etc/file") ); QVERIFY_EQ( std::string("dir/file"), removeExtension("dir/file.txt") ); QVERIFY_EQ( std::string("dir/file"), removeExtension("dir/file") ); QVERIFY_EQ( std::string("dir.ext/file"), removeExtension("dir.ext/file.txt") ); QVERIFY_EQ( std::string("dir.ext/file"), removeExtension("dir.ext/file") ); QVERIFY_EQ( std::string("dir.ext/.dotfile"), removeExtension("dir.ext/.dotfile") ); QVERIFY_EQ( std::string("dir.ext/a"), removeExtension("dir.ext/a.ext") ); } void testReplaceExtension() { QVERIFY_EQ( std::string("file.new"), replaceExtension("file.txt", "new") ); QVERIFY_EQ( std::string("a.new"), replaceExtension("a.txt", "new") ); QVERIFY_EQ( std::string("/etc/file.new"), replaceExtension("/etc/file.txt", "new") ); QVERIFY_EQ( std::string("file.new"), replaceExtension("file", "new") ); QVERIFY_EQ( std::string("/etc/file.new"), replaceExtension("/etc/file", "new") ); QVERIFY_EQ( std::string("dir/file.new"), replaceExtension("dir/file.txt", "new") ); QVERIFY_EQ( std::string("dir/file.new"), replaceExtension("dir/file", "new") ); QVERIFY_EQ( std::string("dir.ext/file.new"), replaceExtension("dir.ext/file.txt", "new") ); QVERIFY_EQ( std::string("dir.ext/file.new"), replaceExtension("dir.ext/file", "new") ); QVERIFY_EQ( std::string("dir.ext/.dotfile.new"), replaceExtension("dir.ext/.dotfile", "new") ); QVERIFY_EQ( std::string("dir.ext/a.new"), replaceExtension("dir.ext/a.ext", "new") ); } void testReplaceBackslash() { std::string str; str = ""; replaceBackslash( str ); QVERIFY_EQ( std::string(""), str ); str = "some str"; replaceBackslash( str ); QVERIFY_EQ( std::string("some str"), str ); str = "\\some\\str\\"; replaceBackslash( str ); QVERIFY_EQ( std::string("/some/str/"), str ); str = "/some/str/"; replaceBackslash( str ); QVERIFY_EQ( std::string("/some/str/"), str ); char cstr[20]; strcpy( cstr, "" ); replaceBackslash( cstr ); QVERIFY_EQ( std::string(""), std::string(cstr) ); strcpy( cstr, "some cstr" ); replaceBackslash( cstr ); QVERIFY_EQ( std::string("some cstr"), std::string(cstr) ); strcpy( cstr, "\\some\\cstr\\" ); replaceBackslash( cstr ); QVERIFY_EQ( std::string("/some/cstr/"), std::string(cstr) ); strcpy( cstr, "/some/cstr/" ); replaceBackslash( cstr ); QVERIFY_EQ( std::string("/some/cstr/"), std::string(cstr) ); } void testReplaceSlash() { std::string str; str = ""; replaceSlash( str ); QVERIFY_EQ( std::string(""), str ); str = "some str"; replaceSlash( str ); QVERIFY_EQ( std::string("some str"), str ); str = "\\some\\str\\"; replaceSlash( str ); QVERIFY_EQ( std::string("\\some\\str\\"), str ); str = "/some/str/"; replaceSlash( str ); QVERIFY_EQ( std::string("\\some\\str\\"), str ); char cstr[20]; strcpy( cstr, "" ); replaceSlash( cstr ); QVERIFY_EQ( std::string(""), std::string(cstr) ); strcpy( cstr, "some cstr" ); replaceSlash( cstr ); QVERIFY_EQ( std::string("some cstr"), std::string(cstr) ); strcpy( cstr, "\\some\\cstr\\" ); replaceSlash( cstr ); QVERIFY_EQ( std::string("\\some\\cstr\\"), std::string(cstr) ); strcpy( cstr, "/some/cstr/" ); replaceSlash( cstr ); QVERIFY_EQ( std::string("\\some\\cstr\\"), std::string(cstr) ); } void testGetRelativePath() { QVERIFY_EQ( std::string("file"), getRelativePath("", "file") ); QVERIFY_EQ( std::string("file"), getRelativePath("/home/dir", "file") ); QVERIFY_EQ( std::string("subdir/file"), getRelativePath("/home/dir", "subdir/file") ); QVERIFY_EQ( std::string("./file"), getRelativePath("/home/dir", "/home/dir/file") ); QVERIFY_EQ( std::string("./file"), getRelativePath("/home/dir/", "/home/dir/file") ); QVERIFY_EQ( std::string("./subdir/file"), getRelativePath("/home/dir/", "/home/dir/subdir/file") ); QVERIFY_EQ( std::string("file"), getRelativePath("\\home\\dir", "file") ); QVERIFY_EQ( std::string("subdir/file"), getRelativePath("\\home\\dir", "subdir\\file") ); QVERIFY_EQ( std::string("./file"), getRelativePath("\\home\\dir", "\\home\\dir\\file") ); QVERIFY_EQ( std::string("./file"), getRelativePath("\\home\\dir\\", "\\home\\dir\\file") ); QVERIFY_EQ( std::string("./subdir/file"), getRelativePath("\\home\\dir\\", "\\home\\dir\\subdir\\file") ); QVERIFY_EQ( std::string("file"), getRelativePath("c:\\home\\dir", "file") ); QVERIFY_EQ( std::string("subdir/file"), getRelativePath("c:\\home\\dir", "subdir\\file") ); QVERIFY_EQ( std::string("./file"), getRelativePath("c:\\home\\dir", "c:\\home\\dir\\file") ); QVERIFY_EQ( std::string("./file"), getRelativePath("c:\\home\\dir\\", "c:\\home\\dir\\file") ); QVERIFY_EQ( std::string("./subdir/file"), getRelativePath("c:\\home\\dir\\", "c:\\home\\dir\\subdir\\file") ); } void testGetAbsolutePath() { QVERIFY_EQ( std::string("/home/dir/file"), getAbsolutePath("/home/dir", "file") ); QVERIFY_EQ( std::string("/home/dir/file"), getAbsolutePath("/home/dir/", "file") ); QVERIFY_EQ( std::string("/home/dir/file"), getAbsolutePath("/home/dir", "./file") ); QVERIFY_EQ( std::string("/home/dir/file"), getAbsolutePath("/home/dir/", "./file") ); QVERIFY_EQ( std::string("/home/file"), getAbsolutePath("/home/dir", "../file") ); QVERIFY_EQ( std::string("/home/file"), getAbsolutePath("/home/dir/", "../file") ); QVERIFY_EQ( std::string("/file"), getAbsolutePath("/home/dir", "../../file") ); QVERIFY_EQ( std::string("/file"), getAbsolutePath("/home/dir/", "../../file") ); QVERIFY_EQ( std::string("/home/dir2/file"), getAbsolutePath("/home/dir", "../dir2/file") ); QVERIFY_EQ( std::string("/home/dir2/file"), getAbsolutePath("/home/dir/", "../dir2/file") ); QVERIFY_EQ( std::string("/file"), getAbsolutePath("/home/dir", "/file") ); QVERIFY_EQ( std::string("/file"), getAbsolutePath("/home/dir/", "/file") ); QVERIFY_EQ( std::string("c:/home/dir/file"), getAbsolutePath("c:\\home\\dir", "file") ); QVERIFY_EQ( std::string("c:/home/dir/file"), getAbsolutePath("c:\\home\\dir\\", "file") ); // FIXME these should probably be / instead /* QVERIFY_EQ( std::string("c:/home/dir/file"), getAbsolutePath("c:\\home\\dir", ".\\file") ); QVERIFY_EQ( std::string("c:/home/dir/file"), getAbsolutePath("c:\\home\\dir\\", ".\\file") ); QVERIFY_EQ( std::string("c:\\file"), getAbsolutePath("c:\\home\\dir", "c:\\file") ); QVERIFY_EQ( std::string("c:\\file"), getAbsolutePath("c:\\home\\dir\\", "c:\\file") ); */ } void testUtf8ChrTrunc() { std::string str; // each char is 3 bytes str = "ひらがな"; utf8chrtrunc( str, 13 ); QVERIFY_EQ( std::string("ひらがな"), str ); str = "ひらがな"; utf8chrtrunc( str, 12 ); QVERIFY_EQ( std::string("ひらがな"), str ); str = "ひらがな"; utf8chrtrunc( str, 11 ); QVERIFY_EQ( std::string("ひらが"), str ); str = "ひらがな"; utf8chrtrunc( str, 10 ); QVERIFY_EQ( std::string("ひらが"), str ); str = "ひらがな"; utf8chrtrunc( str, 9 ); QVERIFY_EQ( std::string("ひらが"), str ); str = "ひらがな"; utf8chrtrunc( str, 8 ); QVERIFY_EQ( std::string("ひら"), str ); char cstr[20]; strcpy( cstr, "ひらがな" ); utf8chrtrunc( cstr, 13 ); QVERIFY_EQ( std::string("ひらがな"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8chrtrunc( cstr, 12 ); QVERIFY_EQ( std::string("ひらがな"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8chrtrunc( cstr, 11 ); QVERIFY_EQ( std::string("ひらが"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8chrtrunc( cstr, 10 ); QVERIFY_EQ( std::string("ひらが"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8chrtrunc( cstr, 9 ); QVERIFY_EQ( std::string("ひらが"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8chrtrunc( cstr, 8 ); QVERIFY_EQ( std::string("ひら"), std::string(cstr) ); } void testUtf8StrTrunc() { std::string str; // each char is 3 bytes str = "ひらがな"; utf8strtrunc( str, 5 ); QVERIFY_EQ( std::string("ひらがな"), str ); str = "ひらがな"; utf8strtrunc( str, 4 ); QVERIFY_EQ( std::string("ひらがな"), str ); str = "ひらがな"; utf8strtrunc( str, 3 ); QVERIFY_EQ( std::string("ひらが"), str ); str = "ひらがな"; utf8strtrunc( str, 2 ); QVERIFY_EQ( std::string("ひら"), str ); str = "ひらがな"; utf8strtrunc( str, 1 ); QVERIFY_EQ( std::string("ひ"), str ); str = "ひらがな"; utf8strtrunc( str, 0 ); QVERIFY_EQ( std::string(""), str ); // mixed length str = "aひbע"; utf8strtrunc( str, 5 ); QVERIFY_EQ( std::string("aひbע"), str ); str = "aひbע"; utf8strtrunc( str, 4 ); QVERIFY_EQ( std::string("aひbע"), str ); str = "aひbע"; utf8strtrunc( str, 3 ); QVERIFY_EQ( std::string("aひb"), str ); str = "aひbע"; utf8strtrunc( str, 2 ); QVERIFY_EQ( std::string("aひ"), str ); str = "aひbע"; utf8strtrunc( str, 1 ); QVERIFY_EQ( std::string("a"), str ); str = "aひbע"; utf8strtrunc( str, 0 ); QVERIFY_EQ( std::string(""), str ); char cstr[20]; strcpy( cstr, "ひらがな" ); utf8strtrunc( cstr, 5 ); QVERIFY_EQ( std::string("ひらがな"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8strtrunc( cstr, 4 ); QVERIFY_EQ( std::string("ひらがな"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8strtrunc( cstr, 3 ); QVERIFY_EQ( std::string("ひらが"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8strtrunc( cstr, 2 ); QVERIFY_EQ( std::string("ひら"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8strtrunc( cstr, 1 ); QVERIFY_EQ( std::string("ひ"), std::string(cstr) ); strcpy( cstr, "ひらがな" ); utf8strtrunc( cstr, 0 ); QVERIFY_EQ( std::string(""), std::string(cstr) ); // mixed length strcpy( cstr, "aひbע" ); utf8strtrunc( cstr, 5 ); QVERIFY_EQ( std::string("aひbע"), std::string(cstr) ); strcpy( cstr, "aひbע" ); utf8strtrunc( cstr, 4 ); QVERIFY_EQ( std::string("aひbע"), std::string(cstr) ); strcpy( cstr, "aひbע" ); utf8strtrunc( cstr, 3 ); QVERIFY_EQ( std::string("aひb"), std::string(cstr) ); strcpy( cstr, "aひbע" ); utf8strtrunc( cstr, 2 ); QVERIFY_EQ( std::string("aひ"), std::string(cstr) ); strcpy( cstr, "aひbע" ); utf8strtrunc( cstr, 1 ); QVERIFY_EQ( std::string("a"), std::string(cstr) ); strcpy( cstr, "aひbע" ); utf8strtrunc( cstr, 0 ); QVERIFY_EQ( std::string(""), std::string(cstr) ); } // FIXME add tests // normalizePath (4 args) // normalizePath (2 args) // getAbsolutePath // fixAbsolutePath // fixFileCase // getFileList // file_exists // is_directory // mkpath // utf8len // utf8strtrunc // utf8chrtrunc }; QTEST_MAIN(MiscTest) #include "misc_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_add_test.cc000066400000000000000000000776661466047437300212430ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests adding and removing model components, along with undo/redo. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" class ModelAddTest : public QObject { Q_OBJECT private: void undoRedo( Model * lhs, Model * rhs1, Model * rhs2 ) { QVERIFY_TRUE( lhs->propEqual( rhs2 ) ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs1 ) ); lhs->redo(); QVERIFY_TRUE( lhs->propEqual( rhs2 ) ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs1 ) ); lhs->redo(); QVERIFY_TRUE( lhs->propEqual( rhs2 ) ); } void addTriangleVertices( Model * m ) { for ( int t = 0; t < 6; ++t ) { double c = (double) (t + 1); m->addVertex( c, c, c ); m->setVertexFree( t, true ); } m->operationComplete( "Add triangle test vertices" ); } private slots: void initTestCase() { log_enable_debug( false ); } void testVertex() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1v = newTestModel(); local_ptr rhs_2v = newTestModel(); local_ptr rhs_3v = newTestModel(); local_ptr rhs_deleted_v1 = newTestModel(); local_ptr rhs_deleted_v2 = newTestModel(); local_ptr rhs_deleted_v3 = newTestModel(); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); QVERIFY_EQ( 0, (int) lhs->getVertexCount() ); lhs->addVertex( 1, 1, 1 ); rhs_1v->addVertex( 1, 1, 1 ); rhs_2v->addVertex( 1, 1, 1 ); rhs_3v->addVertex( 1, 1, 1 ); rhs_deleted_v2->addVertex( 1, 1, 1 ); rhs_deleted_v3->addVertex( 1, 1, 1 ); lhs->operationComplete( "Add Vertex 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1v.get() ) ); QVERIFY_EQ( 1, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1v.get() ); lhs->addVertex( 2, 2, 2 ); rhs_2v->addVertex( 2, 2, 2 ); rhs_3v->addVertex( 2, 2, 2 ); rhs_deleted_v1->addVertex( 2, 2, 2 ); rhs_deleted_v3->addVertex( 2, 2, 2 ); lhs->operationComplete( "Add Vertex 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2v.get() ) ); QVERIFY_EQ( 2, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_1v.get(), rhs_2v.get() ); lhs->addVertex( 3, 3, 3 ); rhs_3v->addVertex( 3, 3, 3 ); rhs_deleted_v1->addVertex( 3, 3, 3 ); rhs_deleted_v2->addVertex( 3, 3, 3 ); lhs->operationComplete( "Add Vertex 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3v.get() ) ); QVERIFY_EQ( 3, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_2v.get(), rhs_3v.get() ); lhs->deleteVertex( 0 ); lhs->operationComplete( "Delete Vertex 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_v1.get() ) ); QVERIFY_EQ( 2, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_3v.get(), rhs_deleted_v1.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3v.get() ) ); QVERIFY_EQ( 3, (int) lhs->getVertexCount() ); lhs->deleteVertex( 1 ); lhs->operationComplete( "Delete Vertex 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_v2.get() ) ); QVERIFY_EQ( 2, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_3v.get(), rhs_deleted_v2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3v.get() ) ); QVERIFY_EQ( 3, (int) lhs->getVertexCount() ); lhs->deleteVertex( 2 ); lhs->operationComplete( "Delete Vertex 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_v3.get() ) ); QVERIFY_EQ( 2, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_3v.get(), rhs_deleted_v3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3v.get() ) ); QVERIFY_EQ( 3, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_2v.get(), rhs_3v.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_1v.get(), rhs_2v.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getVertexCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1v.get() ); } void testTriangle() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1t = newTestModel(); local_ptr rhs_2t = newTestModel(); local_ptr rhs_3t = newTestModel(); local_ptr rhs_deleted_t1 = newTestModel(); local_ptr rhs_deleted_t2 = newTestModel(); local_ptr rhs_deleted_t3 = newTestModel(); addTriangleVertices( lhs.get() ); addTriangleVertices( rhs_empty.get() ); addTriangleVertices( rhs_1t.get() ); addTriangleVertices( rhs_2t.get() ); addTriangleVertices( rhs_3t.get() ); addTriangleVertices( rhs_deleted_t1.get() ); addTriangleVertices( rhs_deleted_t2.get() ); addTriangleVertices( rhs_deleted_t3.get() ); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); lhs->addTriangle( 0, 1, 2 ); rhs_1t->addTriangle( 0, 1, 2 ); rhs_2t->addTriangle( 0, 1, 2 ); rhs_3t->addTriangle( 0, 1, 2 ); rhs_deleted_t2->addTriangle( 0, 1, 2 ); rhs_deleted_t3->addTriangle( 0, 1, 2 ); lhs->operationComplete( "Add Triangle 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1t.get() ) ); QVERIFY_EQ( 1, (int) lhs->getTriangleCount() ); lhs->addTriangle( 3, 4, 5 ); rhs_2t->addTriangle( 3, 4, 5 ); rhs_3t->addTriangle( 3, 4, 5 ); rhs_deleted_t1->addTriangle( 3, 4, 5 ); rhs_deleted_t3->addTriangle( 3, 4, 5 ); lhs->operationComplete( "Add Triangle 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2t.get() ) ); QVERIFY_EQ( 2, (int) lhs->getTriangleCount() ); lhs->addTriangle( 1, 3, 5 ); rhs_3t->addTriangle( 1, 3, 5 ); rhs_deleted_t1->addTriangle( 1, 3, 5 ); rhs_deleted_t2->addTriangle( 1, 3, 5 ); lhs->operationComplete( "Add Triangle 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3t.get() ) ); QVERIFY_EQ( 3, (int) lhs->getTriangleCount() ); lhs->deleteTriangle( 0 ); lhs->operationComplete( "Delete Triangle 1" ); QVERIFY_EQ( 2, (int) lhs->getTriangleCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_t1.get() ) ); undoRedo( lhs.get(), rhs_3t.get(), rhs_deleted_t1.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3t.get() ) ); lhs->deleteTriangle( 1 ); lhs->operationComplete( "Delete Triangle 2" ); QVERIFY_EQ( 2, (int) lhs->getTriangleCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_t2.get() ) ); undoRedo( lhs.get(), rhs_3t.get(), rhs_deleted_t2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3t.get() ) ); lhs->deleteTriangle( 2 ); lhs->operationComplete( "Delete Triangle 3" ); QVERIFY_EQ( 2, (int) lhs->getTriangleCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_t3.get() ) ); undoRedo( lhs.get(), rhs_3t.get(), rhs_deleted_t3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3t.get() ) ); QVERIFY_EQ( 3, (int) lhs->getTriangleCount() ); undoRedo( lhs.get(), rhs_2t.get(), rhs_3t.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getTriangleCount() ); undoRedo( lhs.get(), rhs_1t.get(), rhs_2t.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getTriangleCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1t.get() ); } void testGroup() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); local_ptr rhs_deleted_1 = newTestModel(); local_ptr rhs_deleted_2 = newTestModel(); local_ptr rhs_deleted_3 = newTestModel(); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); lhs->addGroup( "Group 1" ); rhs_1->addGroup( "Group 1" ); rhs_2->addGroup( "Group 1" ); rhs_3->addGroup( "Group 1" ); rhs_deleted_2->addGroup( "Group 1" ); rhs_deleted_3->addGroup( "Group 1" ); lhs->operationComplete( "Add Group 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1.get() ) ); QVERIFY_EQ( 1, (int) lhs->getGroupCount() ); lhs->addGroup( "Group 2" ); rhs_2->addGroup( "Group 2" ); rhs_3->addGroup( "Group 2" ); rhs_deleted_1->addGroup( "Group 2" ); rhs_deleted_3->addGroup( "Group 2" ); lhs->operationComplete( "Add Group 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2.get() ) ); QVERIFY_EQ( 2, (int) lhs->getGroupCount() ); lhs->addGroup( "Group 3" ); rhs_3->addGroup( "Group 3" ); rhs_deleted_1->addGroup( "Group 3" ); rhs_deleted_2->addGroup( "Group 3" ); lhs->operationComplete( "Add Group 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getGroupCount() ); lhs->deleteGroup( 0 ); lhs->operationComplete( "Delete Group 1" ); QVERIFY_EQ( 2, (int) lhs->getGroupCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_1.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_1.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteGroup( 1 ); lhs->operationComplete( "Delete Group 2" ); QVERIFY_EQ( 2, (int) lhs->getGroupCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_2.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteGroup( 2 ); lhs->operationComplete( "Delete Group 3" ); QVERIFY_EQ( 2, (int) lhs->getGroupCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_3.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getGroupCount() ); undoRedo( lhs.get(), rhs_2.get(), rhs_3.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getGroupCount() ); undoRedo( lhs.get(), rhs_1.get(), rhs_2.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getGroupCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1.get() ); } void testMaterial() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); local_ptr rhs_deleted_1 = newTestModel(); local_ptr rhs_deleted_2 = newTestModel(); local_ptr rhs_deleted_3 = newTestModel(); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); lhs->addColorMaterial( "Material 1" ); rhs_1->addColorMaterial( "Material 1" ); rhs_2->addColorMaterial( "Material 1" ); rhs_3->addColorMaterial( "Material 1" ); rhs_deleted_2->addColorMaterial( "Material 1" ); rhs_deleted_3->addColorMaterial( "Material 1" ); lhs->operationComplete( "Add Material 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1.get() ) ); QVERIFY_EQ( 1, (int) lhs->getTextureCount() ); lhs->addColorMaterial( "Material 2" ); rhs_2->addColorMaterial( "Material 2" ); rhs_3->addColorMaterial( "Material 2" ); rhs_deleted_1->addColorMaterial( "Material 2" ); rhs_deleted_3->addColorMaterial( "Material 2" ); lhs->operationComplete( "Add Material 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2.get() ) ); QVERIFY_EQ( 2, (int) lhs->getTextureCount() ); lhs->addColorMaterial( "Material 3" ); rhs_3->addColorMaterial( "Material 3" ); rhs_deleted_1->addColorMaterial( "Material 3" ); rhs_deleted_2->addColorMaterial( "Material 3" ); lhs->operationComplete( "Add Material 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getTextureCount() ); lhs->deleteTexture( 0 ); lhs->operationComplete( "Delete Material 1" ); QVERIFY_EQ( 2, (int) lhs->getTextureCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_1.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_1.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteTexture( 1 ); lhs->operationComplete( "Delete Material 2" ); QVERIFY_EQ( 2, (int) lhs->getTextureCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_2.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteTexture( 2 ); lhs->operationComplete( "Delete Material 3" ); QVERIFY_EQ( 2, (int) lhs->getTextureCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_3.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getTextureCount() ); undoRedo( lhs.get(), rhs_2.get(), rhs_3.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getTextureCount() ); undoRedo( lhs.get(), rhs_1.get(), rhs_2.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getTextureCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1.get() ); } void testJoint() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); local_ptr rhs_deleted_2 = newTestModel(); local_ptr rhs_deleted_3 = newTestModel(); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); lhs->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_1->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_2->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_3->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_deleted_2->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_deleted_3->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0, -1 ); lhs->operationComplete( "Add Joint 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1.get() ) ); QVERIFY_EQ( 1, (int) lhs->getBoneJointCount() ); lhs->addBoneJoint( "Joint 2", 2, 2, 2, 0, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 2", 2, 2, 2, 0, 0, 0, 0 ); rhs_3->addBoneJoint( "Joint 2", 2, 2, 2, 0, 0, 0, 0 ); rhs_deleted_3->addBoneJoint( "Joint 2", 2, 2, 2, 0, 0, 0, 0 ); lhs->operationComplete( "Add Joint 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2.get() ) ); QVERIFY_EQ( 2, (int) lhs->getBoneJointCount() ); lhs->addBoneJoint( "Joint 3", 3, 3, 3, 0, 0, 0, 0 ); rhs_3->addBoneJoint( "Joint 3", 3, 3, 3, 0, 0, 0, 0 ); rhs_deleted_2->addBoneJoint( "Joint 3", 3, 3, 3, 0, 0, 0, 0 ); lhs->operationComplete( "Add Joint 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getBoneJointCount() ); lhs->deleteBoneJoint( 1 ); lhs->operationComplete( "Delete Joint 2" ); QVERIFY_EQ( 2, (int) lhs->getBoneJointCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_2.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteBoneJoint( 2 ); lhs->operationComplete( "Delete Joint 3" ); QVERIFY_EQ( 2, (int) lhs->getBoneJointCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_3.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getBoneJointCount() ); undoRedo( lhs.get(), rhs_2.get(), rhs_3.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getBoneJointCount() ); undoRedo( lhs.get(), rhs_1.get(), rhs_2.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getBoneJointCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1.get() ); } void testPoint() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); local_ptr rhs_deleted_1 = newTestModel(); local_ptr rhs_deleted_2 = newTestModel(); local_ptr rhs_deleted_3 = newTestModel(); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); lhs->addPoint( "Point 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_1->addPoint( "Point 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_2->addPoint( "Point 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_3->addPoint( "Point 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_deleted_2->addPoint( "Point 1", 1, 1, 1, 0, 0, 0, -1 ); rhs_deleted_3->addPoint( "Point 1", 1, 1, 1, 0, 0, 0, -1 ); lhs->operationComplete( "Add Point 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1.get() ) ); QVERIFY_EQ( 1, (int) lhs->getPointCount() ); lhs->addPoint( "Point 2", 2, 2, 2, 0, 0, 0, -1 ); rhs_2->addPoint( "Point 2", 2, 2, 2, 0, 0, 0, -1 ); rhs_3->addPoint( "Point 2", 2, 2, 2, 0, 0, 0, -1 ); rhs_deleted_1->addPoint( "Point 2", 2, 2, 2, 0, 0, 0, -1 ); rhs_deleted_3->addPoint( "Point 2", 2, 2, 2, 0, 0, 0, -1 ); lhs->operationComplete( "Add Point 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2.get() ) ); QVERIFY_EQ( 2, (int) lhs->getPointCount() ); lhs->addPoint( "Point 3", 3, 3, 3, 0, 0, 0, -1 ); rhs_3->addPoint( "Point 3", 3, 3, 3, 0, 0, 0, -1 ); rhs_deleted_1->addPoint( "Point 3", 3, 3, 3, 0, 0, 0, -1 ); rhs_deleted_2->addPoint( "Point 3", 3, 3, 3, 0, 0, 0, -1 ); lhs->operationComplete( "Add Point 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getPointCount() ); lhs->deletePoint( 0 ); lhs->operationComplete( "Delete Point 1" ); QVERIFY_EQ( 2, (int) lhs->getPointCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_1.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_1.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deletePoint( 1 ); lhs->operationComplete( "Delete Point 2" ); QVERIFY_EQ( 2, (int) lhs->getPointCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_2.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deletePoint( 2 ); lhs->operationComplete( "Delete Point 3" ); QVERIFY_EQ( 2, (int) lhs->getPointCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_3.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getPointCount() ); undoRedo( lhs.get(), rhs_2.get(), rhs_3.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getPointCount() ); undoRedo( lhs.get(), rhs_1.get(), rhs_2.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getPointCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1.get() ); } void testProjection() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); local_ptr rhs_deleted_1 = newTestModel(); local_ptr rhs_deleted_2 = newTestModel(); local_ptr rhs_deleted_3 = newTestModel(); QVERIFY_TRUE( lhs->propEqual( rhs_empty.get() ) ); lhs->addProjection( "Projection 1", Model::TPT_Cylinder, 1, 1, 1); rhs_1->addProjection( "Projection 1", Model::TPT_Cylinder, 1, 1, 1); rhs_2->addProjection( "Projection 1", Model::TPT_Cylinder, 1, 1, 1); rhs_3->addProjection( "Projection 1", Model::TPT_Cylinder, 1, 1, 1); rhs_deleted_2->addProjection( "Projection 1", Model::TPT_Cylinder, 1, 1, 1); rhs_deleted_3->addProjection( "Projection 1", Model::TPT_Cylinder, 1, 1, 1); lhs->operationComplete( "Add Projection 1" ); QVERIFY_TRUE( lhs->propEqual( rhs_1.get() ) ); QVERIFY_EQ( 1, (int) lhs->getProjectionCount() ); lhs->addProjection( "Projection 2", Model::TPT_Cylinder, 2, 2, 2); rhs_2->addProjection( "Projection 2", Model::TPT_Cylinder, 2, 2, 2); rhs_3->addProjection( "Projection 2", Model::TPT_Cylinder, 2, 2, 2); rhs_deleted_1->addProjection( "Projection 2", Model::TPT_Cylinder, 2, 2, 2); rhs_deleted_3->addProjection( "Projection 2", Model::TPT_Cylinder, 2, 2, 2); lhs->operationComplete( "Add Projection 2" ); QVERIFY_TRUE( lhs->propEqual( rhs_2.get() ) ); QVERIFY_EQ( 2, (int) lhs->getProjectionCount() ); lhs->addProjection( "Projection 3", Model::TPT_Cylinder, 3, 3, 3); rhs_3->addProjection( "Projection 3", Model::TPT_Cylinder, 3, 3, 3); rhs_deleted_1->addProjection( "Projection 3", Model::TPT_Cylinder, 3, 3, 3); rhs_deleted_2->addProjection( "Projection 3", Model::TPT_Cylinder, 3, 3, 3); lhs->operationComplete( "Add Projection 3" ); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getProjectionCount() ); lhs->deleteProjection( 0 ); lhs->operationComplete( "Delete Projection 1" ); QVERIFY_EQ( 2, (int) lhs->getProjectionCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_1.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_1.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteProjection( 1 ); lhs->operationComplete( "Delete Projection 2" ); QVERIFY_EQ( 2, (int) lhs->getProjectionCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_2.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_2.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); lhs->deleteProjection( 2 ); lhs->operationComplete( "Delete Projection 3" ); QVERIFY_EQ( 2, (int) lhs->getProjectionCount() ); QVERIFY_TRUE( lhs->propEqual( rhs_deleted_3.get() ) ); undoRedo( lhs.get(), rhs_3.get(), rhs_deleted_3.get() ); lhs->undo(); QVERIFY_TRUE( lhs->propEqual( rhs_3.get() ) ); QVERIFY_EQ( 3, (int) lhs->getProjectionCount() ); undoRedo( lhs.get(), rhs_2.get(), rhs_3.get() ); lhs->undo(); QVERIFY_EQ( 2, (int) lhs->getProjectionCount() ); undoRedo( lhs.get(), rhs_1.get(), rhs_2.get() ); lhs->undo(); QVERIFY_EQ( 1, (int) lhs->getProjectionCount() ); undoRedo( lhs.get(), rhs_empty.get(), rhs_1.get() ); } void testForceAddOrDelete() { local_ptr m = newTestModel(); m->forceAddOrDelete( false ); // Make sure we can add/delete before having frame anims. QVERIFY_EQ( 0, m->addVertex( 1, 1, 1 ) ); QVERIFY_EQ( 1, m->addVertex( 2, 2, 2 ) ); QVERIFY_EQ( 2, m->addVertex( 3, 3, 3 ) ); QVERIFY_EQ( 3, (int) m->getVertexCount() ); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_FRAME, "frame anim" ) ); // Now creates and deletes should fail QVERIFY_EQ( -1, m->addVertex( 4, 4, 4 ) ); QVERIFY_EQ( 3, (int) m->getVertexCount() ); QVERIFY_EQ( -1, m->addTriangle( 0, 1, 2 ) ); QVERIFY_EQ( 0, (int) m->getTriangleCount() ); QVERIFY_EQ( -1, m->addGroup( "failed group" ) ); QVERIFY_EQ( 0, (int) m->getGroupCount() ); QVERIFY_EQ( -1, m->addPoint( "failed point", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 0, (int) m->getPointCount() ); QVERIFY_EQ( 0, m->addColorMaterial( "first material" ) ); QVERIFY_EQ( 1, (int) m->getTextureCount() ); QVERIFY_EQ( 0, m->addBoneJoint( "first joint", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getBoneJointCount() ); QVERIFY_EQ( 0, m->addProjection( "first projection", Model::TPT_Cylinder, 1, 1, 1 ) ); QVERIFY_EQ( 1, (int) m->getProjectionCount() ); m->forceAddOrDelete( true ); QVERIFY_EQ( 3, m->addVertex( 4, 4, 4 ) ); QVERIFY_EQ( 4, (int) m->getVertexCount() ); QVERIFY_EQ( 0, m->addTriangle( 0, 1, 2 ) ); QVERIFY_EQ( 1, (int) m->getTriangleCount() ); QVERIFY_EQ( 0, m->addGroup( "success group" ) ); QVERIFY_EQ( 1, (int) m->getGroupCount() ); QVERIFY_EQ( 0, m->addPoint( "success point", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getPointCount() ); QVERIFY_EQ( 1, m->addColorMaterial( "second material" ) ); QVERIFY_EQ( 2, (int) m->getTextureCount() ); QVERIFY_EQ( 1, m->addBoneJoint( "second joint", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 2, (int) m->getBoneJointCount() ); QVERIFY_EQ( 1, m->addProjection( "second projection", Model::TPT_Cylinder, 1, 1, 1 ) ); QVERIFY_EQ( 2, (int) m->getProjectionCount() ); // FIXME test delete m->forceAddOrDelete( false ); m->deleteVertex(0); QVERIFY_EQ( 4, (int) m->getVertexCount() ); m->deleteTriangle(0); QVERIFY_EQ( 1, (int) m->getTriangleCount() ); m->deleteGroup(0); QVERIFY_EQ( 1, (int) m->getGroupCount() ); m->deletePoint(0); QVERIFY_EQ( 1, (int) m->getPointCount() ); m->deleteTexture(0); QVERIFY_EQ( 1, (int) m->getTextureCount() ); m->deleteBoneJoint(1); QVERIFY_EQ( 1, (int) m->getBoneJointCount() ); m->deleteProjection(0); QVERIFY_EQ( 1, (int) m->getProjectionCount() ); m->forceAddOrDelete( true ); m->deleteVertex(0); QVERIFY_EQ( 3, (int) m->getVertexCount() ); m->deleteTriangle(0); QVERIFY_EQ( 0, (int) m->getTriangleCount() ); m->deleteGroup(0); QVERIFY_EQ( 0, (int) m->getGroupCount() ); m->deletePoint(0); QVERIFY_EQ( 0, (int) m->getPointCount() ); m->deleteTexture(0); QVERIFY_EQ( 0, (int) m->getTextureCount() ); m->deleteBoneJoint(0); QVERIFY_EQ( 0, (int) m->getBoneJointCount() ); m->deleteProjection(0); QVERIFY_EQ( 0, (int) m->getProjectionCount() ); } void testVertexFailure() { local_ptr m = newTestModel(); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); QVERIFY_EQ( 0, m->addVertex( 0, 0, 0 ) ); // Works QVERIFY_EQ( 1, (int) m->getVertexCount() ); // In Animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addVertex( 1, 1, 1 ) ); QVERIFY_EQ( 1, (int) m->getVertexCount() ); } void testTriangleFailure() { local_ptr m = newTestModel(); m->addVertex( 1, 1, 1 ); m->addVertex( 2, 2, 2 ); m->addVertex( 3, 3, 3 ); m->addVertex( 4, 4, 4 ); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); // Works QVERIFY_EQ( 0, m->addTriangle( 1, 2, 3 ) ); QVERIFY_EQ( 1, (int) m->getTriangleCount() ); // Vertex out of range QVERIFY_EQ( -1, m->addTriangle( 1, 2, 4 ) ); QVERIFY_EQ( 1, (int) m->getTriangleCount() ); // In animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addTriangle( 1, 2, 3 ) ); QVERIFY_EQ( 1, (int) m->getTriangleCount() ); } void testGroupFailure() { local_ptr m = newTestModel(); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); QVERIFY_EQ( 0, m->addGroup( "group name" ) ); QVERIFY_EQ( 1, (int) m->getGroupCount() ); // NULL name QVERIFY_EQ( -1, m->addGroup( NULL ) ); QVERIFY_EQ( 1, (int) m->getGroupCount() ); // In animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addVertex( 1, 1, 1 ) ); QVERIFY_EQ( 1, (int) m->getGroupCount() ); } void testMaterialFailure() { local_ptr m = newTestModel(); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); QVERIFY_EQ( 0, m->addColorMaterial( "material name" ) ); QVERIFY_EQ( 1, (int) m->getTextureCount() ); // NULL texture QVERIFY_EQ( -1, m->addTexture( NULL ) ); QVERIFY_EQ( 1, (int) m->getTextureCount() ); // NULL name QVERIFY_EQ( -1, m->addColorMaterial( NULL ) ); QVERIFY_EQ( 1, (int) m->getTextureCount() ); // In animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addColorMaterial( "failed material" ) ); QVERIFY_EQ( 1, (int) m->getTextureCount() ); } void testJointFailure() { local_ptr m = newTestModel(); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); QVERIFY_EQ( 0, m->addBoneJoint( "joint name", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getBoneJointCount() ); // Bad parent QVERIFY_EQ( -1, m->addBoneJoint( "failed joint", 1, 1, 1, 0, 0, 0, 1 ) ); QVERIFY_EQ( 1, (int) m->getBoneJointCount() ); // NULL name QVERIFY_EQ( -1, m->addBoneJoint( NULL, 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getBoneJointCount() ); // In animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addBoneJoint( "anim joint", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getBoneJointCount() ); } void testPointFailure() { local_ptr m = newTestModel(); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); QVERIFY_EQ( 0, m->addPoint( "point name", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getPointCount() ); // Bad parent QVERIFY_EQ( -1, m->addPoint( "failed point", 1, 1, 1, 0, 0, 0, 1 ) ); QVERIFY_EQ( 1, (int) m->getPointCount() ); // NULL name QVERIFY_EQ( -1, m->addPoint( NULL, 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getPointCount() ); // In animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addPoint( "anim point", 1, 1, 1, 0, 0, 0, -1 ) ); QVERIFY_EQ( 1, (int) m->getPointCount() ); } void testProjectionFailure() { local_ptr m = newTestModel(); QVERIFY_EQ( 0, m->addAnimation( Model::ANIMMODE_SKELETAL, "anim name" ) ); QVERIFY_EQ( 0, m->addProjection( "point name", Model::TPT_Cylinder, 1, 1, 1 ) ); QVERIFY_EQ( 1, (int) m->getProjectionCount() ); // NULL name QVERIFY_EQ( -1, m->addProjection( NULL, Model::TPT_Cylinder, 1, 1, 1 ) ); QVERIFY_EQ( 1, (int) m->getProjectionCount() ); // In animation mode m->setCurrentAnimation( Model::ANIMMODE_SKELETAL, (unsigned int) 0 ); QVERIFY_EQ( -1, m->addProjection( "anim point", Model::TPT_Cylinder, 1, 1, 1 ) ); QVERIFY_EQ( 1, (int) m->getProjectionCount() ); } // FIXME deletion // * Delete selected // * Remove orphaned vertices // * Remove flattened triangles // * Index adjustments // FIXME test joint reparenting and vertex/point assignments // FIXME undo/redo release }; QTEST_MAIN(ModelAddTest) #include "model_add_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_anim_test.cc000066400000000000000000000424601466047437300214170ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests animation methods in the Model class #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" Model * loadModelOrDie( ModelFilter & filter, const char * filename ) { Model * model = new Model; model->forceAddOrDelete( true ); Model::ModelErrorE err = filter.readFile( model, filename ); if ( err != Model::ERROR_NONE ) { fprintf( stderr, "fatal: read %s: %s\n", filename, Model::errorToString( err ) ); delete model; exit( -1 ); } model->loadTextures(); model->forceAddOrDelete( true ); return model; } Model * loadMm3dOrDie( const char * filename, FileFactory * factory = NULL ) { MisfitFilter f; if ( factory ) f.setFactory( factory ); return loadModelOrDie( f, filename ); } class ModelAnimTest : public QObject { Q_OBJECT private: Model * loadTestModel() { Model * model = loadMm3dOrDie( "data/model_equal_test.mm3d" ); model->setUndoEnabled( true ); return model; } private slots: void initTestCase() { log_enable_debug( false ); log_enable_warning( true ); } void testAnimAdd() { for ( int m = Model::ANIMMODE_SKELETAL; m <= Model::ANIMMODE_FRAME; ++m ) { const Model::AnimationModeE mode = static_cast( m ); local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); QVERIFY_EQ( 0, (int) lhs->getAnimCount( mode ) ); lhs->addAnimation( mode, "Animation A" ); rhs_1->addAnimation( mode, "Animation A" ); rhs_2->addAnimation( mode, "Animation A" ); rhs_3->addAnimation( mode, "Animation A" ); QVERIFY_EQ( 1, (int) lhs->getAnimCount( mode ) ); lhs->operationComplete( "Add animation A" ); lhs->addAnimation( mode, "Animation B" ); rhs_2->addAnimation( mode, "Animation B" ); rhs_3->addAnimation( mode, "Animation B" ); QVERIFY_EQ( 2, (int) lhs->getAnimCount( mode ) ); lhs->operationComplete( "Add animation B" ); lhs->addAnimation( mode, "Animation C" ); rhs_3->addAnimation( mode, "Animation C" ); QVERIFY_EQ( 3, (int) lhs->getAnimCount( mode ) ); lhs->operationComplete( "Add animation C" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } } void testAnimDelete() { for ( int m = Model::ANIMMODE_SKELETAL; m <= Model::ANIMMODE_FRAME; ++m ) { const Model::AnimationModeE mode = static_cast( m ); local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); rhs_list.push_back( rhs_empty.get() ); lhs->addAnimation( mode, "Animation A" ); rhs_1->addAnimation( mode, "Animation A" ); rhs_2->addAnimation( mode, "Animation A" ); rhs_3->addAnimation( mode, "Animation A" ); lhs->addAnimation( mode, "Animation B" ); rhs_1->addAnimation( mode, "Animation B" ); lhs->addAnimation( mode, "Animation C" ); rhs_1->addAnimation( mode, "Animation C" ); rhs_2->addAnimation( mode, "Animation C" ); QVERIFY_EQ( 3, (int) lhs->getAnimCount( mode ) ); lhs->operationComplete( "Add animations" ); lhs->deleteAnimation( mode, 1 ); lhs->operationComplete( "Delete animation B" ); QVERIFY_EQ( 2, (int) lhs->getAnimCount( mode ) ); lhs->deleteAnimation( mode, 1 ); lhs->operationComplete( "Delete animation C" ); QVERIFY_EQ( 1, (int) lhs->getAnimCount( mode ) ); lhs->deleteAnimation( mode, 0 ); lhs->operationComplete( "Delete animation A" ); QVERIFY_EQ( 0, (int) lhs->getAnimCount( mode ) ); checkUndoRedo( 4, lhs.get(), rhs_list ); } } void testAnimSetName() { for ( int m = Model::ANIMMODE_SKELETAL; m <= Model::ANIMMODE_FRAME; ++m ) { const Model::AnimationModeE mode = static_cast( m ); local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); lhs->addAnimation( mode, "Animation A" ); rhs_1->addAnimation( mode, "Animation A" ); rhs_2->addAnimation( mode, "Animation A" ); rhs_3->addAnimation( mode, "Rename A" ); lhs->addAnimation( mode, "Animation B" ); rhs_1->addAnimation( mode, "Animation B" ); rhs_2->addAnimation( mode, "Rename B" ); rhs_3->addAnimation( mode, "Rename B" ); lhs->operationComplete( "Add animations" ); QVERIFY_EQ( std::string("Animation A"), std::string(lhs->getAnimName(mode, 0))); QVERIFY_EQ( std::string("Animation B"), std::string(lhs->getAnimName(mode, 1))); lhs->setAnimName( mode, 1, "Rename B" ); lhs->operationComplete( "Rename animation B" ); QVERIFY_EQ( std::string("Animation A"), std::string(lhs->getAnimName(mode, 0))); QVERIFY_EQ( std::string("Rename B"), std::string(lhs->getAnimName(mode, 1))); lhs->setAnimName( mode, 0, "Rename A" ); lhs->operationComplete( "Rename animation A" ); QVERIFY_EQ( std::string("Rename A"), std::string(lhs->getAnimName(mode, 0))); QVERIFY_EQ( std::string("Rename B"), std::string(lhs->getAnimName(mode, 1))); checkUndoRedo( 3, lhs.get(), rhs_list ); } } void testAnimSetFPS() { for ( int m = Model::ANIMMODE_SKELETAL; m <= Model::ANIMMODE_FRAME; ++m ) { const Model::AnimationModeE mode = static_cast( m ); local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); lhs->addAnimation( mode, "Animation A" ); rhs_1->addAnimation( mode, "Animation A" ); rhs_2->addAnimation( mode, "Animation A" ); rhs_3->addAnimation( mode, "Animation A" ); lhs->addAnimation( mode, "Animation B" ); rhs_1->addAnimation( mode, "Animation B" ); rhs_2->addAnimation( mode, "Animation B" ); rhs_3->addAnimation( mode, "Animation B" ); lhs->operationComplete( "Add animations" ); double defaultFps = mode == Model::ANIMMODE_FRAME ? 10.0 : 30.0; QVERIFY_TRUE( float_equiv(defaultFps, lhs->getAnimFPS(mode, 0))); QVERIFY_TRUE( float_equiv(defaultFps, lhs->getAnimFPS(mode, 1))); lhs->setAnimFPS( mode, 0, 7.0 ); rhs_2->setAnimFPS( mode, 0, 7.0 ); rhs_3->setAnimFPS( mode, 0, 7.0 ); lhs->operationComplete( "Set FPS" ); QVERIFY_TRUE( float_equiv(7.0, lhs->getAnimFPS(mode, 0))); QVERIFY_TRUE( float_equiv(defaultFps, lhs->getAnimFPS(mode, 1))); lhs->setAnimFPS( mode, 1, 8.0 ); rhs_3->setAnimFPS( mode, 1, 8.0 ); lhs->operationComplete( "Set FPS" ); QVERIFY_TRUE( float_equiv(7.0, lhs->getAnimFPS(mode, 0))); QVERIFY_TRUE( float_equiv(8.0, lhs->getAnimFPS(mode, 1))); checkUndoRedo( 3, lhs.get(), rhs_list ); } } void testSkelAnimSetFrameCount() { local_ptr lhs = loadTestModel(); local_ptr rhs_1 = loadTestModel(); local_ptr rhs_2 = loadTestModel(); local_ptr rhs_3 = loadTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); const Model::AnimationModeE mode = Model::ANIMMODE_SKELETAL; const int animIndex = 1; QVERIFY_EQ( 20, (int) lhs->getAnimFrameCount( mode, 0 ) ); QVERIFY_EQ( 8, (int) lhs->getAnimFrameCount( mode, 1 ) ); const int reduceCount = 4; lhs->setAnimFrameCount( mode, animIndex, reduceCount ); rhs_2->setAnimFrameCount( mode, animIndex, reduceCount ); rhs_3->setAnimFrameCount( mode, animIndex, reduceCount ); QVERIFY_EQ( 20, (int) lhs->getAnimFrameCount( mode, 0 ) ); QVERIFY_EQ( reduceCount, (int) lhs->getAnimFrameCount( mode, 1 ) ); lhs->operationComplete( "Reduce frame count" ); const int increaseCount = 10; lhs->setAnimFrameCount( mode, animIndex, increaseCount ); rhs_3->setAnimFrameCount( mode, animIndex, increaseCount ); QVERIFY_EQ( 20, (int) lhs->getAnimFrameCount( mode, 0 ) ); QVERIFY_EQ( increaseCount, (int) lhs->getAnimFrameCount( mode, 1 ) ); lhs->operationComplete( "Increase frame count" ); // Make sure increasing the frame count gave us blank frames // (instead of restoring deleted frames). const int bcount = lhs->getBoneJointCount(); for ( int f = reduceCount; f < increaseCount; f++ ) { for ( int b = 0; b < bcount; ++b ) { QVERIFY_FALSE( lhs->hasSkelAnimKeyframe( animIndex, f, b, true ) ); QVERIFY_FALSE( lhs->hasSkelAnimKeyframe( animIndex, f, b, false ) ); } } checkUndoRedo( 2, lhs.get(), rhs_list ); } void testFrameAnimSetFrameCount() { local_ptr lhs = loadTestModel(); local_ptr rhs_1 = loadTestModel(); local_ptr rhs_2 = loadTestModel(); local_ptr rhs_3 = loadTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); const Model::AnimationModeE mode = Model::ANIMMODE_FRAME; const int animIndex = 1; QVERIFY_EQ( 20, (int) lhs->getAnimFrameCount( mode, 0 ) ); QVERIFY_EQ( 8, (int) lhs->getAnimFrameCount( mode, 1 ) ); const int reduceCount = 4; lhs->setAnimFrameCount( mode, animIndex, reduceCount ); rhs_2->setAnimFrameCount( mode, animIndex, reduceCount ); rhs_3->setAnimFrameCount( mode, animIndex, reduceCount ); QVERIFY_EQ( 20, (int) lhs->getAnimFrameCount( mode, 0 ) ); QVERIFY_EQ( reduceCount, (int) lhs->getAnimFrameCount( mode, 1 ) ); lhs->operationComplete( "Reduce frame count" ); const int increaseCount = 10; lhs->setAnimFrameCount( mode, animIndex, increaseCount ); rhs_3->setAnimFrameCount( mode, animIndex, increaseCount ); QVERIFY_EQ( 20, (int) lhs->getAnimFrameCount( mode, 0 ) ); QVERIFY_EQ( increaseCount, (int) lhs->getAnimFrameCount( mode, 1 ) ); lhs->operationComplete( "Increase frame count" ); // Make sure increasing the frame count gave us blank frames // (instead of restoring deleted frames). double coords[3] = { 0, 0, 0 }; double acoords[3] = { 0, 0, 0 }; const int vcount = lhs->getVertexCount(); for ( int f = reduceCount; f < increaseCount; f++ ) { for ( int v = 0; v < vcount; ++v ) { lhs->getVertexCoordsUnanimated( v, coords ); lhs->getFrameAnimVertexCoords( animIndex, f, v, acoords[0], acoords[1], acoords[2] ); QVERIFY_ARRAY_EQ( coords, 3, acoords, 3 ); } } checkUndoRedo( 2, lhs.get(), rhs_list ); } void testFrameAnimSetPosition() { // Vertex { local_ptr lhs = loadTestModel(); local_ptr rhs_1 = loadTestModel(); local_ptr rhs_2 = loadTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); const int animIndex = 1; const int animFrame = 2; const int animVertex = 3; lhs->setFrameAnimVertexCoords( animIndex, animFrame, animVertex, 1.0, 2.0, 3.0 ); rhs_2->setFrameAnimVertexCoords( animIndex, animFrame, animVertex, 1.0, 2.0, 3.0 ); lhs->operationComplete( "Move vertex" ); checkUndoRedo( 1, lhs.get(), rhs_list ); } // Point Translation { local_ptr lhs = loadTestModel(); local_ptr rhs_1 = loadTestModel(); local_ptr rhs_2 = loadTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); const int animIndex = 1; const int animFrame = 2; const int animPoint = 0; lhs->setFrameAnimPointCoords( animIndex, animFrame, animPoint, 1.0, 2.0, 3.0 ); rhs_2->setFrameAnimPointCoords( animIndex, animFrame, animPoint, 1.0, 2.0, 3.0 ); lhs->operationComplete( "Move point" ); checkUndoRedo( 1, lhs.get(), rhs_list ); } // Point Rotation { local_ptr lhs = loadTestModel(); local_ptr rhs_1 = loadTestModel(); local_ptr rhs_2 = loadTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); const int animIndex = 1; const int animFrame = 2; const int animPoint = 0; lhs->setFrameAnimPointRotation( animIndex, animFrame, animPoint, 1.0, 2.0, 3.0 ); rhs_2->setFrameAnimPointRotation( animIndex, animFrame, animPoint, 1.0, 2.0, 3.0 ); lhs->operationComplete( "Rotate point" ); checkUndoRedo( 1, lhs.get(), rhs_list ); } } // FIXME add tests: // X add animation // X delete animation // x set anim name // x set anim frame count // x increase // x decrease // x get // x set anim fps // - with keyframes (check frame time adjusted) // get // ? set frame anim point count // ? set frame anim vertex count // - set frame anim point coords // x with point // ? without point // get frame anim point coords // - set frame anim point rot // x with point // ? without point // get frame anim point rot // - set frame anim vertex coords // x with vertex // ? without vertex // get // get normals // - set quick frame anim vertex coords // with vertex // - without vertex (test with undo !?) // - copy animation // - split animation // - join animations // - merge animations // - move animation // - convert anim to frame // - clear anim frame // set skel anim keyframe // add new keyframe (trans & rot) // update existing keyframe (trans & rot) // hasSkelAnimKeyframe // getSkelAnimKeyframe // interpSkelAnimKeyframe // interpSkelAnimKeyframeTime // with looping // - without looping // - delete skel anim keyframe // insert frame anim frame // - not at end of list // - set current animation by name // set current animation by index // combine name + index (is name even used?) // get // - set no animation // set current animation frame // get // set current animation time // get // - set/get looping // setup joints // x get anim count // x get anim name // undo }; QTEST_MAIN(ModelAnimTest) #include "model_anim_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_equal_test.cc000066400000000000000000001650141466047437300216030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests the equality functions of the model components (inner // classes), and the whole model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" class ModelEqualTest : public QObject { Q_OBJECT private: void vertexIsInitialized( Model::Vertex * v ) { QVERIFY_TRUE( v->m_visible ); QVERIFY_FALSE( v->m_selected ); QVERIFY_FALSE( v->m_free ); QVERIFY( v->m_drawSource == v->m_coord ); } void triangleIsInitialized( Model::Triangle * t ) { QVERIFY_TRUE( t->m_visible ); QVERIFY_FALSE( t->m_selected ); QVERIFY_EQ( -1, t->m_projection ); // FIXME need fuzzy EQ for floats QVERIFY( t->m_flatSource == t->m_flatNormals ); QVERIFY( t->m_normalSource[0] == t->m_finalNormals[0] ); QVERIFY( t->m_normalSource[1] == t->m_finalNormals[1] ); QVERIFY( t->m_normalSource[2] == t->m_finalNormals[2] ); } void groupIsInitialized( Model::Group * g ) { QVERIFY_EQ( std::string(""), g->m_name ); QVERIFY_EQ( -1, g->m_materialIndex ); QVERIFY_EQ( (size_t) 0, g->m_triangleIndices.size() ); QVERIFY_EQ( (uint8_t) 255, g->m_smooth ); QVERIFY_EQ( (uint8_t) 180, g->m_angle ); QVERIFY_TRUE( g->m_visible ); QVERIFY_FALSE( g->m_selected ); } void materialIsInitialized( Model::Material * m ) { QVERIFY_EQ( std::string(""), m->m_name ); QVERIFY_EQ( Model::Material::MATTYPE_TEXTURE, m->m_type ); // FIXME need fuzzy EQ for floats QVERIFY_FALSE( m->m_sClamp ); QVERIFY_FALSE( m->m_tClamp ); QVERIFY_EQ( (GLuint) 0, m->m_texture ); QVERIFY_EQ( std::string(""), m->m_filename ); QVERIFY_EQ( std::string(""), m->m_alphaFilename ); QVERIFY_TRUE( NULL == m->m_textureData ); } void initCompareInfluence( Model::InfluenceT * inf ) { inf->m_boneId = 0; inf->m_type = Model::IT_Custom; inf->m_weight = 1.0; } void initCompareVertex( Model::Vertex * v ) { v->m_visible = true; v->m_selected = false; v->m_free = true; v->m_coord[0] = 3.0; v->m_coord[1] = 4.0; v->m_coord[2] = 5.0; Model::InfluenceT inf; initCompareInfluence( &inf ); v->m_influences.clear(); v->m_influences.push_back( inf ); } void initCompareTriangle( Model::Triangle * tri ) { tri->m_visible = true; tri->m_selected = false; tri->m_projection = -1; tri->m_vertexIndices[0] = 3; tri->m_vertexIndices[1] = 4; tri->m_vertexIndices[2] = 5; tri->m_s[0] = 0.0; tri->m_s[1] = 0.5; tri->m_s[2] = 1.0; tri->m_t[0] = 1.0; tri->m_t[1] = 0.5; tri->m_t[2] = 0.0; } void initCompareGroup( Model::Group * grp ) { grp->m_name = "Group"; grp->m_visible = true; grp->m_selected = false; grp->m_angle = 90; grp->m_smooth = 127; grp->m_materialIndex = 1; grp->m_triangleIndices.clear(); grp->m_triangleIndices.insert(2); grp->m_triangleIndices.insert(4); grp->m_triangleIndices.insert(6); } void initCompareMaterial( Model::Material * mat ) { mat->m_name = "Material"; mat->m_filename = "filename.tga"; mat->m_alphaFilename = "alpha.tga"; mat->m_type = Model::Material::MATTYPE_BLANK; mat->m_ambient[0] = 0.10; mat->m_ambient[1] = 0.11; mat->m_ambient[2] = 0.12; mat->m_ambient[3] = 0.13; mat->m_diffuse[0] = 0.20; mat->m_diffuse[1] = 0.21; mat->m_diffuse[2] = 0.22; mat->m_diffuse[3] = 0.23; mat->m_specular[0] = 0.30; mat->m_specular[1] = 0.31; mat->m_specular[2] = 0.32; mat->m_specular[3] = 0.33; mat->m_emissive[0] = 0.40; mat->m_emissive[1] = 0.41; mat->m_emissive[2] = 0.42; mat->m_emissive[3] = 0.43; mat->m_shininess = 0.5; // Color is unused mat->m_sClamp = false; mat->m_tClamp = false; mat->m_texture = 0; } void initCompareMaterialAndTexture( Model::Material * mat, Texture * tex, bool alpha ) { initCompareMaterial( mat ); static uint8_t data[] = { 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xdd, 0xdd, 0xdd, 0xcc, 0xcc, 0xcc, }; static uint8_t alphaData[] = { 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x88, 0xff, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x44, 0xff, 0xff, 0xff, 0xff, 0xee, 0xee, 0xee, 0xff, 0xdd, 0xdd, 0xdd, 0xff, 0xcc, 0xcc, 0xcc, 0x00, }; mat->m_type = Model::Material::MATTYPE_TEXTURE; mat->m_textureData = tex; tex->m_format = alpha ? Texture::FORMAT_RGBA : Texture::FORMAT_RGB; tex->m_data = alpha ? alphaData : data; tex->m_width = 4; tex->m_height = 4; tex->m_isBad = false; tex->m_origWidth = 16; tex->m_origHeight = 16; } uint8_t * copyTextureData( Model::Material * mat ) { Texture * tex = mat->m_textureData; size_t dataLen = ( tex->m_format == Texture::FORMAT_RGBA ) ? 4 * 4 * 4 // With alpha channel ( h * w * depth ) : 4 * 4 * 3; // Without alpha channel ( h * w * depth ) uint8_t * d = new uint8_t[ dataLen ]; memcpy( d, tex->m_data, dataLen ); tex->m_data = d; return d; } void initCompareJoint( Model::Joint * j ) { j->m_name = "Joint"; j->m_visible = true; j->m_selected = false; j->m_parent = 0; j->m_localRotation[0] = 33.3; j->m_localRotation[1] = 44.4; j->m_localRotation[2] = 55.5; j->m_localTranslation[0] = 3.0; j->m_localTranslation[1] = 4.0; j->m_localTranslation[2] = 5.0; } void initComparePoint( Model::Point * p ) { p->m_name = "Point"; p->m_type = 0; p->m_visible = true; p->m_selected = false; p->m_trans[0] = 3.0; p->m_trans[1] = 4.0; p->m_trans[2] = 5.0; p->m_rot[0] = 33.3; p->m_rot[1] = 44.4; p->m_rot[2] = 55.5; Model::InfluenceT inf; initCompareInfluence( &inf ); p->m_influences.clear(); p->m_influences.push_back( inf ); } void initCompareProjection( Model::TextureProjection * p ) { p->m_name = "Projection"; p->m_type = Model::TPT_Sphere; p->m_selected = false; p->m_pos[0] = 3.0; p->m_pos[1] = 4.0; p->m_pos[2] = 5.0; p->m_upVec[0] = 0.0; p->m_upVec[1] = 1.0; p->m_upVec[2] = 0.0; p->m_seamVec[0] = 0.0; p->m_seamVec[1] = 0.0; p->m_seamVec[2] = 1.0; // min x/y p->m_range[0][0] = 0.0; p->m_range[0][1] = 0.25; // max x/y p->m_range[1][0] = 1.0; p->m_range[1][1] = 0.75; } void initCompareBackground( Model::BackgroundImage * p ) { p->m_filename = "background.jpg"; p->m_scale = 1.0; p->m_center[0] = 3.0; p->m_center[1] = 4.0; p->m_center[2] = 5.0; } void initCompareKeyframe( Model::Keyframe * kf ) { kf->m_jointIndex = 1; kf->m_frame = 2; kf->m_time = 1.0 / 30.0; kf->m_isRotation = false; kf->m_parameter[0] = 3.0; kf->m_parameter[1] = 4.0; kf->m_parameter[2] = 5.0; } void initCompareSkelAnim( Model::SkelAnim * sa ) { sa->releaseData(); // This releases any stale keyframes sa->m_name = "Skeletal"; sa->m_fps = 30.0; sa->m_spf = 1.0 / sa->m_fps; sa->m_loop = true; sa->m_frameCount = 5; Model::Keyframe * kf = Model::Keyframe::get(); initCompareKeyframe( kf ); sa->m_jointKeyframes.resize( 3 ); sa->m_jointKeyframes[kf->m_jointIndex].insert_sorted( kf ); } void initCompareFrameVertex( Model::FrameAnimVertex * fav ) { fav->m_coord[0] = 3.0; fav->m_coord[1] = 4.0; fav->m_coord[2] = 5.0; } void initCompareFramePoint( Model::FrameAnimPoint * fap ) { fap->m_trans[0] = 3.0; fap->m_trans[1] = 4.0; fap->m_trans[2] = 5.0; fap->m_rot[0] = 30.0; fap->m_rot[1] = 40.0; fap->m_rot[2] = 50.0; } void initCompareFrameData( Model::FrameAnimData * fad ) { fad->releaseData(); if ( !fad->m_frameVertices ) fad->m_frameVertices = new Model::FrameAnimVertexList; if ( !fad->m_framePoints ) fad->m_framePoints = new Model::FrameAnimPointList; Model::FrameAnimVertex * fav; fav = Model::FrameAnimVertex::get(); initCompareFrameVertex(fav); fad->m_frameVertices->push_back( fav ); fav = Model::FrameAnimVertex::get(); initCompareFrameVertex(fav); fav->m_coord[0] += 10.0; fav->m_coord[1] += 10.0; fav->m_coord[2] += 10.0; fad->m_frameVertices->push_back( fav ); Model::FrameAnimPoint * fap; fap = Model::FrameAnimPoint::get(); initCompareFramePoint(fap); fad->m_framePoints->push_back( fap ); fap = Model::FrameAnimPoint::get(); initCompareFramePoint(fap); fap->m_trans[0] += 20.0; fap->m_trans[1] += 20.0; fap->m_trans[2] += 20.0; fap->m_rot[0] += 90.0; fap->m_rot[1] += 90.0; fap->m_rot[2] += 90.0; fad->m_framePoints->push_back( fap ); } void initCompareFrameAnim( Model::FrameAnim * fa ) { fa->releaseData(); fa->m_name = "Frame"; fa->m_fps = 10.0; Model::FrameAnimData * fad; fad = new Model::FrameAnimData; initCompareFrameData( fad ); fa->m_frameData.push_back( fad ); fad = new Model::FrameAnimData; initCompareFrameData( fad ); fa->m_frameData.push_back( fad ); fad = new Model::FrameAnimData; initCompareFrameData( fad ); fa->m_frameData.push_back( fad ); } private slots: void initTestCase() { log_enable_debug( false ); } // Many primitives are recycled. Test initial conditions, change conditions, // release, and re-get to make sure that the recyled primitives are properly // initialized. void testInfluenceCompare() { Model::InfluenceT lhs; Model::InfluenceT rhs; initCompareInfluence( &lhs ); initCompareInfluence( &rhs ); QVERIFY_TRUE( lhs == rhs ); rhs.m_boneId = 2; QVERIFY_FALSE( lhs == rhs ); initCompareInfluence( &rhs ); QVERIFY_TRUE( lhs == rhs ); rhs.m_type = Model::IT_Remainder; QVERIFY_FALSE( lhs == rhs ); initCompareInfluence( &rhs ); QVERIFY_TRUE( lhs == rhs ); rhs.m_weight = 0.5; QVERIFY_FALSE( lhs == rhs ); } void testVertexCompare() { Model::Vertex * lhs = Model::Vertex::get(); Model::Vertex * rhs = Model::Vertex::get(); initCompareVertex( lhs ); int bits = 0; bits = Model::PropVisibility; initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_visible = false; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropSelection; initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_selected = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropFree; initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_free = false; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropCoords; for ( int i = 0; i < 3; ++i ) { initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_coord[i] = lhs->m_coord[i] - 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } bits = Model::PropInfluences; initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_type = Model::IT_Remainder; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_weight = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.push_back( rhs->m_influences.back() ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.clear(); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); bits = Model::PropWeights; initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_type = Model::IT_Remainder; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_weight = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.push_back( rhs->m_influences.back() ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initCompareVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.clear(); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); lhs->release(); rhs->release(); } void testTriangleCompare() { Model::Triangle * lhs = Model::Triangle::get(); Model::Triangle * rhs = Model::Triangle::get(); initCompareTriangle( lhs ); int bits = 0; bits = Model::PropVisibility; initCompareTriangle( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_visible = false; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropSelection; initCompareTriangle( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_selected = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropProjections; initCompareTriangle( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_projection = 0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropTexCoords; for ( int i = 0; i < 3; ++i ) { initCompareTriangle( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_s[i] = rhs->m_s[i] + 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareTriangle( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_t[i] = rhs->m_t[i] + 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } bits = Model::PropVertices; for ( int i = 0; i < 3; ++i ) { initCompareTriangle( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_vertexIndices[i] += 1; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } lhs->release(); rhs->release(); } void testGroupCompare() { Model::Group * lhs = Model::Group::get(); Model::Group * rhs = Model::Group::get(); initCompareGroup( lhs ); int bits = 0; bits = Model::PropName; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "group"; // different case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropVisibility; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_visible = false; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropSelection; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_selected = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropMaterials; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_materialIndex = 2; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropNormals; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_angle = 45; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropNormals; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_smooth = 128; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropTriangles; initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 2 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 6 ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 2 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 6 ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 1 ); rhs->m_triangleIndices.insert( 2 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 6 ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 2 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 6 ); rhs->m_triangleIndices.insert( 7 ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 2 ); rhs->m_triangleIndices.insert( 6 ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 2 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 5 ); rhs->m_triangleIndices.insert( 6 ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); // Same triangles in a different order, this is legal for propEqual models initCompareGroup( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_triangleIndices.clear(); rhs->m_triangleIndices.insert( 6 ); rhs->m_triangleIndices.insert( 4 ); rhs->m_triangleIndices.insert( 2 ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); lhs->release(); rhs->release(); } void testMaterialCompare() { Model::Material * lhs = Model::Material::get(); Model::Material * rhs = Model::Material::get(); initCompareMaterial( lhs ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_texture = 2; // This only matters to OpenGL QVERIFY_TRUE( lhs->propEqual( *rhs ) ); int bits = 0; bits = Model::PropName; initCompareMaterial( lhs ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "material"; // different case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropPaths; initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_filename = "filename.jpg"; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterial( lhs ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_alphaFilename = "alpha.gif"; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropType; initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_type = Model::Material::MATTYPE_TEXTURE; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropLighting; for ( int i = 0; i < 4; i++ ) { initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_ambient[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_diffuse[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_specular[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_emissive[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_shininess = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropClamp; initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_sClamp = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterial( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_tClamp = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); // FIXME these checks really belong in the texture testing code. I do want to keep // at least one check below to make sure that the propEquality test fails if the // texture compare fails. local_ptr tex_lhs = new Texture(); local_ptr tex_rhs = new Texture(); bits = Model::PropPixels; initCompareMaterialAndTexture( lhs, tex_lhs.get(), false ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), false ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_format = Texture::FORMAT_RGBA; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterialAndTexture( lhs, tex_lhs.get(), true ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_format = Texture::FORMAT_RGB; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_width = 2; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_height = 2; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_origWidth = 2; // Compare doesn't care rhs->m_textureData->m_origHeight = 2; QVERIFY_TRUE( lhs->propEqual( *rhs ) ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_isBad = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); lhs->m_textureData->m_isBad = true; // Even if both are bad, it's not a match rhs->m_textureData->m_isBad = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareMaterialAndTexture( lhs, tex_lhs.get(), true ); for ( int i = 0; i < 4; ++i ) { initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); local_array buf = copyTextureData( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_data[i] = 0x48; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); tex_rhs->m_data = NULL; } // The last element has an alpha value of 0, so changes to the color channels // should not result in a texture mismatch. for ( int i = 0; i < 3; ++i ) { initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); local_array buf = copyTextureData( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_data[15 * 4 + i] = 0x48; QVERIFY_TRUE( lhs->propEqual( *rhs ) ); // Clear, don't care tex_rhs->m_data = NULL; } // Same as above, except the alpha channel is no longer clear. for ( int i = 0; i < 3; ++i ) { initCompareMaterialAndTexture( lhs, tex_lhs.get(), true ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), true ); local_array buf = copyTextureData( rhs ); local_array buf2 = copyTextureData( lhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_data[15 * 4 + i] = 0x48; rhs->m_textureData->m_data[15 * 4 + 3] = 0x01; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); tex_rhs->m_data = NULL; tex_lhs->m_data = NULL; } initCompareMaterialAndTexture( lhs, tex_lhs.get(), false ); for ( int i = 0; i < 3; ++i ) { initCompareMaterialAndTexture( rhs, tex_rhs.get(), false ); local_array buf = copyTextureData( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_data[i] = 0x48; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); tex_rhs->m_data = NULL; } for ( int i = 0; i < 3; ++i ) { initCompareMaterialAndTexture( lhs, tex_lhs.get(), false ); initCompareMaterialAndTexture( rhs, tex_rhs.get(), false ); local_array buf = copyTextureData( rhs ); local_array buf2 = copyTextureData( lhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_textureData->m_data[i] = 0x48; rhs->m_textureData->m_data[3] = 0x00; // Not alpha, change above matters lhs->m_textureData->m_data[3] = 0x00; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); tex_rhs->m_data = NULL; tex_lhs->m_data = NULL; } // Test image data is statically allocated, don't free it tex_lhs->m_data = NULL; tex_rhs->m_data = NULL; lhs->release(); rhs->release(); } void testJointCompare() { Model::Joint * lhs = Model::Joint::get(); Model::Joint * rhs = Model::Joint::get(); initCompareJoint( lhs ); int bits = 0; bits = Model::PropName; initCompareJoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "joint"; // case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropVisibility; initCompareJoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_visible = false; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropSelection; initCompareJoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_selected = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = 0; initCompareJoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_parent = 1; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, ~bits ) ); for ( int i = 0; i < 3; ++i ) { bits = Model::PropRotation; initCompareJoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_localRotation[i] = 11.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropCoords; initCompareJoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_localTranslation[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } lhs->release(); rhs->release(); } void testPointCompare() { Model::Point * lhs = Model::Point::get(); Model::Point * rhs = Model::Point::get(); initComparePoint( lhs ); int bits = 0; bits = Model::PropName; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "point"; // case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropType; // Point m_type doesn't actually do anything, but check it anyway. initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_type = 2; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropVisibility; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_visible = false; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropSelection; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_selected = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); for ( int i = 0; i < 3; ++i ) { bits = Model::PropRotation; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_rot[i] = 11.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropCoords; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_trans[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } bits = Model::PropInfluences; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_type = Model::IT_Remainder; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_weight = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.push_back( rhs->m_influences.back() ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.clear(); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); bits = Model::PropWeights; initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_type = Model::IT_Remainder; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.front().m_weight = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.push_back( rhs->m_influences.back() ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); initComparePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_influences.clear(); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~(Model::PropWeights | Model::PropInfluences) ) ); lhs->release(); rhs->release(); } void testKeyframeCompare() { Model::Keyframe * lhs = Model::Keyframe::get(); Model::Keyframe * rhs = Model::Keyframe::get(); initCompareKeyframe( lhs ); int bits = 0; bits = 0; initCompareKeyframe( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_jointIndex = 2; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropTime; initCompareKeyframe( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_frame = 3; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareKeyframe( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_time = 2.0 / 30.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); for ( int i = 0; i < 3; i++ ) { bits = Model::PropRotation; initCompareKeyframe( rhs ); lhs->m_isRotation = true; rhs->m_isRotation = true; QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_parameter[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropCoords; initCompareKeyframe( rhs ); lhs->m_isRotation = false; rhs->m_isRotation = false; QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_parameter[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } // Restore m_isRotation to proper state initCompareKeyframe( lhs ); lhs->release(); rhs->release(); } void testSkelAnimCompare() { Model::SkelAnim * lhs = Model::SkelAnim::get(); Model::SkelAnim * rhs = Model::SkelAnim::get(); initCompareSkelAnim( lhs ); int bits = 0; bits = Model::PropName; initCompareSkelAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "skeletal"; // case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropTime; initCompareSkelAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_fps = 24.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropDimensions; initCompareSkelAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_frameCount = 4; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropType; // Change keyframe initCompareSkelAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_jointKeyframes[1][0]->m_isRotation = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); Model::Keyframe * kf; bits = Model::PropCoords | Model::PropRotation | Model::PropType; // Add same keyframe for a different joint initCompareSkelAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); kf = Model::Keyframe::get(); initCompareKeyframe( kf ); kf->m_jointIndex = 0; rhs->m_jointKeyframes[kf->m_jointIndex].insert_sorted( kf ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); // Add identical keyframe as rotation for the same joint initCompareSkelAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); kf = Model::Keyframe::get(); initCompareKeyframe( kf ); kf->m_isRotation = true; rhs->m_jointKeyframes[kf->m_jointIndex].insert_sorted( kf ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); lhs->release(); rhs->release(); } void testFrameVertexCompare() { Model::FrameAnimVertex * lhs = Model::FrameAnimVertex::get(); Model::FrameAnimVertex * rhs = Model::FrameAnimVertex::get(); initCompareFrameVertex( lhs ); int bits = 0; bits = Model::PropCoords; for ( int i = 0; i < 3; i++ ) { initCompareFrameVertex( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_coord[i] = 0.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } lhs->release(); rhs->release(); } void testFramePointCompare() { Model::FrameAnimPoint * lhs = Model::FrameAnimPoint::get(); Model::FrameAnimPoint * rhs = Model::FrameAnimPoint::get(); initCompareFramePoint( lhs ); int bits = 0; for ( int i = 0; i < 3; i++ ) { bits = Model::PropCoords; initCompareFramePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_trans[i] = 0.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropRotation; initCompareFramePoint( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_rot[i] = 0.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } lhs->release(); rhs->release(); } void testFrameDataCompare() { Model::FrameAnimData lhs; Model::FrameAnimData rhs; initCompareFrameData( &lhs ); int bits = 0; bits = Model::PropCoords; initCompareFrameData( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); (*rhs.m_frameVertices)[0]->m_coord[0] = 0.0; QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); initCompareFrameData( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); (*rhs.m_framePoints)[0]->m_trans[0] = 0.0; QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); bits = Model::PropRotation; initCompareFrameData( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); (*rhs.m_framePoints)[0]->m_rot[0] = 0.0; QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); bits = Model::PropCoords | Model::PropRotation; initCompareFrameData( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); Model::FrameAnimVertex * fav = Model::FrameAnimVertex::get(); initCompareFrameVertex( fav ); rhs.m_frameVertices->push_back(fav); QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); initCompareFrameData( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); Model::FrameAnimPoint * fap = Model::FrameAnimPoint::get(); initCompareFramePoint( fap ); rhs.m_framePoints->push_back(fap); QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); } void testFrameAnimCompare() { Model::FrameAnim * lhs = Model::FrameAnim::get(); Model::FrameAnim * rhs = Model::FrameAnim::get(); initCompareFrameAnim( lhs ); int bits = 0; bits = Model::PropName; initCompareFrameAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "frame"; // case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropTime; initCompareFrameAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_fps = 24.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropCoords | Model::PropRotation; initCompareFrameAnim( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); Model::FrameAnimData * fad = new Model::FrameAnimData; initCompareFrameData( fad ); rhs->m_frameData.push_back( fad ); QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); lhs->release(); rhs->release(); } void testProjectionCompare() { Model::TextureProjection * lhs = Model::TextureProjection::get(); Model::TextureProjection * rhs = Model::TextureProjection::get(); initCompareProjection( lhs ); int bits = 0; bits = Model::PropName; initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_name = "projection"; // case QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropSelection; initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_selected = true; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropType; initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_type = Model::TPT_Cylinder; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); for ( int i = 0; i < 3; i++ ) { bits = Model::PropCoords; initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_pos[i] = 1.0; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); bits = Model::PropRotation; initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_upVec[i] = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_seamVec[i] = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } for ( int i = 0; i < 2; i++ ) { bits = Model::PropDimensions; initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_range[0][i] = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); initCompareProjection( rhs ); QVERIFY_TRUE( lhs->propEqual( *rhs ) ); rhs->m_range[1][i] = 0.5; QVERIFY_FALSE( lhs->propEqual( *rhs ) ); QVERIFY_FALSE( lhs->propEqual( *rhs, bits ) ); QVERIFY_TRUE( lhs->propEqual( *rhs, ~bits ) ); } lhs->release(); rhs->release(); } void testBackgroundCompare() { Model::BackgroundImage lhs; Model::BackgroundImage rhs; initCompareBackground( &lhs ); int bits = 0; bits = Model::PropPaths; initCompareBackground( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); rhs.m_filename = "background.png"; QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); bits = Model::PropScale; initCompareBackground( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); rhs.m_scale = 0.5; QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); bits = Model::PropCoords; for ( int i = 0; i < 3; i++ ) { initCompareBackground( &rhs ); QVERIFY_TRUE( lhs.propEqual( rhs ) ); rhs.m_center[i] = 1.0; QVERIFY_FALSE( lhs.propEqual( rhs ) ); QVERIFY_FALSE( lhs.propEqual( rhs, bits ) ); QVERIFY_TRUE( lhs.propEqual( rhs, ~bits ) ); } } void testModelCompare() { // FIXME test for specific CompareBits matches const char model_file[] = "data/model_equal_test.mm3d"; local_ptr lhs = loadModelOrDie( model_file ); local_ptr rhs; int parts = Model::PartAll; int props = Model::PropAll; // Vertices { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->moveVertex( 1, 3, 4, 5 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setVertexFree( 3, true ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addVertex( 3, 4, 5 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Triangles { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setTriangleVertices( 0, 2, 4, 6 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->selectTriangle( 3 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addTriangle( 3, 4, 5 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Groups { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setGroupSmooth( 1, 160 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addGroup("new group"); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Materials { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setTextureShininess( 1, 55.0f ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addColorMaterial("new material"); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Skeleton { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setBoneJointName( 1, "renamed joint" ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addBoneJoint("new joint", 0, 0, 0, // position 0, 0, 0, // rotation 0 ); // parent joint QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Meta data { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->updateMetaData( "key_1", "new value" ); QVERIFY_EQ( lhs->getMetaDataCount(), rhs->getMetaDataCount() ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addMetaData( "key_3", "new value" ); QVERIFY_EQ( lhs->getMetaDataCount() + 1, rhs->getMetaDataCount() ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Texture projections { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); double seam[3] = { 0, 1, 0 }; rhs->setProjectionSeam( 0, seam ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addProjection( "New Projection", Model::TPT_Cylinder, 0.0, 0.0, 0.0 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Background images { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setBackgroundScale( 0, 0.25f ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setBackgroundImage( 1, "data/test_rgba_comp.tga" ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } // Skeletal animation { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setSkelAnimKeyframe( 0, 0, 0, false, 1.0, 1.0, 1.0 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->deleteSkelAnimKeyframe( 0, 0, 0, false ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addAnimation( Model::ANIMMODE_SKELETAL, "new anin" ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } { rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setFrameAnimVertexCoords( 0, 0, 0, 1.0, 1.0, 1.0 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->setAnimFrameCount( Model::ANIMMODE_FRAME, 0, 25 ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); rhs = loadModelOrDie( model_file ); QVERIFY_TRUE( lhs->propEqual( rhs.get(), parts, props ) ); rhs->addAnimation( Model::ANIMMODE_FRAME, "new anin" ); QVERIFY_FALSE( lhs->propEqual( rhs.get(), parts, props ) ); } } }; QTEST_MAIN(ModelEqualTest) #include "model_equal_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_equiv_test.cc000066400000000000000000002245401466047437300216250ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests the equality functions of the model components (inner // classes), and the whole model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "tgatex.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" Texture * loadTextureOrDie( TextureFilter * f, const char * filename ) { Texture * tex = new Texture; Texture::ErrorE err = f->readFile( tex, filename ); if ( err != Texture::ERROR_NONE ) { fprintf( stderr, "fatal: %s: %s\n", filename, Texture::errorToString( err ) ); delete tex; exit( -1 ); } return tex; } Texture * loadTgaOrDie( const char * filename ) { TgaTextureFilter f; return loadTextureOrDie( &f, filename ); } class ModelEquivTest : public QObject { Q_OBJECT private: private slots: void initTestCase() { log_enable_debug( false ); log_enable_warning( true ); log_enable_error( false ); } // Many primitives are recycled. Test initial conditions, change conditions, // release, and re-get to make sure that the recyled primitives are properly // initialized. void testEmpty() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testExactCompare() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 2, 1, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Invert triangles 0 and 1 in rhs { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 2, 1, 0 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } // Checks that one triangle doesn't match two triangles in the other model. void testNoDouble() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 2, 1, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testDoulbedTriangle() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testOffset() { // Rotate the triangle's vertex indices and see if they still match { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 2, 0, 1 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 1, 2, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testVertexIndex() { // Re-order the vertex indices and see if they still match { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 2, 2, 2 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 0, 0, 0 ); rhs->addTriangle( 2, 1, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 2, 2, 2 ); rhs->addVertex( 1, 1, 1 ); rhs->addTriangle( 0, 2, 1 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testInvertedNormal() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 2, 1, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 1, 0, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 2, 1 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testUnusedGroup() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Unused 1" ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Unused 2" ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testBothGrouped() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Group A" ); lhs->addTriangleToGroup( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Group B" ); rhs->addTriangleToGroup( 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Group A" ); lhs->addGroup( "Group B" ); lhs->addTriangleToGroup( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Group A" ); rhs->addGroup( "Group B" ); rhs->addTriangleToGroup( 1, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Group A" ); lhs->addGroup( "Group B" ); lhs->addTriangleToGroup( 1, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Group A" ); rhs->addGroup( "Group B" ); rhs->addTriangleToGroup( 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testOneGrouped() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Unused 1" ); lhs->addTriangleToGroup( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Unused 2" ); rhs->addTriangleToGroup( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testGroupMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Group A" ); lhs->setGroupSmooth( 0, 1 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Group A" ); rhs->setGroupSmooth( 0, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); lhs->addTriangleToGroup( 0, 0 ); rhs->addTriangleToGroup( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Group A" ); lhs->setGroupAngle( 0, 1 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Group A" ); rhs->setGroupAngle( 0, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); lhs->addTriangleToGroup( 0, 0 ); rhs->addTriangleToGroup( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testGroupSplit() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); lhs->addGroup( "Single" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTriangleToGroup( 0, 1 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 2, 1, 0 ); rhs->addGroup( "Group A" ); rhs->addGroup( "Group B" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTriangleToGroup( 1, 1 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); lhs->addGroup( "Single" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTriangleToGroup( 0, 1 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 2, 1, 0 ); rhs->addGroup( "Group A" ); rhs->addGroup( "Group B" ); rhs->addTriangleToGroup( 0, 1 ); rhs->addTriangleToGroup( 1, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); lhs->addGroup( "Group A" ); lhs->addGroup( "Group B" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTriangleToGroup( 1, 1 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 2, 1, 0 ); rhs->addGroup( "Single" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTriangleToGroup( 0, 1 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); lhs->addGroup( "Group A" ); lhs->addGroup( "Group B" ); lhs->addTriangleToGroup( 0, 1 ); lhs->addTriangleToGroup( 1, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addTriangle( 2, 1, 0 ); rhs->addGroup( "Single" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTriangleToGroup( 0, 1 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testCoordMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 1 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 0 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 0 ); rhs->addTriangle( 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testMaterialMatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addColorMaterial( "Left Mat" ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addColorMaterial( "Right Mat" ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Texture file contents differ, but pixel data matches { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr tex2 = loadTgaOrDie( "data/test_rgb_uncomp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex2.get() ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testMaterialUnmatched() { // Material property mismatch { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addColorMaterial( "Left Mat" ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureShininess( 0, 0.5f ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addColorMaterial( "Right Mat" ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureShininess( 0, 0.6f ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Right Material unassigned { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addColorMaterial( "Left Mat" ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addColorMaterial( "Right Mat" ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Left material unassigned { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addColorMaterial( "Left Mat" ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addColorMaterial( "Right Mat" ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Right group unassigned { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addColorMaterial( "Left Mat" ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addColorMaterial( "Right Mat" ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Left group unassigned { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addColorMaterial( "Left Mat" ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addColorMaterial( "Right Mat" ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Texture data mismatch { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr tex2 = loadTgaOrDie( "data/test_rgba_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex2.get() ); rhs->setGroupTextureId( 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testTexCoordMatch() { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.1, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.8 ); rhs->setTextureCoords( 0, 2, 0.3, 0.7 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testTexCoordMismatch() { { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.2, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.8 ); rhs->setTextureCoords( 0, 2, 0.3, 0.7 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.1, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.9 ); rhs->setTextureCoords( 0, 2, 0.3, 0.7 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.1, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.8 ); rhs->setTextureCoords( 0, 2, 0.1, 0.7 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testTexCoordMismatchIgnored() { // No group material { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setTextureCoords( 0, 0, 0.2, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.8 ); rhs->setTextureCoords( 0, 2, 0.3, 0.7 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Triangle not grouped { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.1, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.9 ); rhs->setTextureCoords( 0, 2, 0.3, 0.7 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Material is not texture map { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addColorMaterial( "Mat Left" ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addColorMaterial( "Mat Right" ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.1, 0.9 ); rhs->setTextureCoords( 0, 1, 0.2, 0.8 ); rhs->setTextureCoords( 0, 2, 0.1, 0.7 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testTexCoordOffset() { // Vert and tex coords rotated, match { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 2, 0, 1 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 0, 0.3, 0.7 ); rhs->setTextureCoords( 0, 1, 0.1, 0.9 ); rhs->setTextureCoords( 0, 2, 0.2, 0.8 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Only tex coords rotated, no match { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addGroup( "Left Group" ); lhs->addTriangleToGroup( 0, 0 ); lhs->addTexture( tex1.get() ); lhs->setGroupTextureId( 0, 0 ); lhs->setTextureCoords( 0, 0, 0.1, 0.9 ); lhs->setTextureCoords( 0, 1, 0.2, 0.8 ); lhs->setTextureCoords( 0, 2, 0.3, 0.7 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addGroup( "Right Group" ); rhs->addTriangleToGroup( 0, 0 ); rhs->addTexture( tex1.get() ); rhs->setGroupTextureId( 0, 0 ); rhs->setTextureCoords( 0, 2, 0.1, 0.9 ); rhs->setTextureCoords( 0, 0, 0.2, 0.8 ); rhs->setTextureCoords( 0, 1, 0.3, 0.7 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } // FIXME test influences void testPointExact() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testOnePointDoubled() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addPoint( "Left2", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right2", 5, 6, 7, 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testBothPointsDoubled() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addPoint( "Left2", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right2", 5, 6, 7, 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addPoint( "Left2", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right2", 5, 6, 7, 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testPointsInverted() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addPoint( "Left2", 4, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 4, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right2", 5, 6, 7, 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addPoint( "Left2", 5, 6, 7, 0, 3, 2 ); rhs->addPoint( "Right2", 5, 6, 7, 0, 3, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 2 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testPointCoordMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 6, 6, 7, 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 5, 7, 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 5, 0, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testPointRotMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 1, 1, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 0, 2 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addPoint( "Right", 5, 6, 7, 0, 1, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } // Test points where rotations angles are not equal, but describe // the same orientation. void testPointRotEquiv() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 0, 0 ); rhs->addPoint( "Right", 5, 6, 7, 0, 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, PI, 0, 0 ); rhs->addPoint( "Right", 5, 6, 7, -PI, 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, PI, 0 ); rhs->addPoint( "Right", 5, 6, 7, 0, -PI, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 0, PI ); rhs->addPoint( "Right", 5, 6, 7, 0, 0, -PI ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 2*PI, 0, 0 ); rhs->addPoint( "Right", 5, 6, 7, 0, 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 2*PI, 0 ); rhs->addPoint( "Right", 5, 6, 7, 0, 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Left", 5, 6, 7, 0, 0, 2*PI ); rhs->addPoint( "Right", 5, 6, 7, 0, 0, 0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testJointExact() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addBoneJoint( "Left A", 2, 3, 4, 1, 1, 1, 0); lhs->addBoneJoint( "Left B", 3, 4, 5, 2, 2, 2, 0); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right A", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right B", 3, 4, 5, 2, 2, 2, 0); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testOneJointDoubled() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addBoneJoint( "Left A", 2, 3, 4, 1, 1, 1, 0); lhs->addBoneJoint( "Left B", 3, 4, 5, 2, 2, 2, 0); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right A", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right B", 3, 4, 5, 2, 2, 2, 0); rhs->addBoneJoint( "Right B", 3, 4, 5, 2, 2, 2, 0); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addBoneJoint( "Left A", 2, 3, 4, 1, 1, 1, 0); lhs->addBoneJoint( "Left B", 3, 4, 5, 2, 2, 2, 0); lhs->addBoneJoint( "Left B", 3, 4, 5, 2, 2, 2, 0); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right A", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right B", 3, 4, 5, 2, 2, 2, 0); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testBothJointsDoubled() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addBoneJoint( "Left A", 2, 3, 4, 1, 1, 1, 0); lhs->addBoneJoint( "Left B", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right A", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right B", 2, 3, 4, 1, 1, 1, 0); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testJointsInverted() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addBoneJoint( "Left A", 2, 3, 4, 1, 1, 1, 0); lhs->addBoneJoint( "Left B", 3, 4, 5, 2, 2, 2, 0); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right B", 3, 4, 5, 2, 2, 2, 0); rhs->addBoneJoint( "Right A", 2, 3, 4, 1, 1, 1, 0); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); lhs->addBoneJoint( "Left B", 3, 4, 5, 2, 2, 2, 0); lhs->addBoneJoint( "Left A", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right A", 2, 3, 4, 1, 1, 1, 0); rhs->addBoneJoint( "Right B", 3, 4, 5, 2, 2, 2, 0); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testJointCoordMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right", 6, 6, 7, 0, 1, 2 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right", 5, 5, 7, 0, 1, 2 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right", 5, 6, 5, 0, 1, 2 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testJointRotMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right", 5, 6, 7, 1, 1, 2 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 0, 2 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 1, 2 ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 1, 1 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } // Test joints where rotations angles are not equal, but describe // the same orientation. void testJointRotEquiv() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, PI, 0, 0 ); rhs->addBoneJoint( "Right", 5, 6, 7, -PI, 0, 0 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, PI, 0 ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, -PI, 0 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 0, PI ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 0, -PI ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 2*PI, 0, 0 ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 0, 0 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 2*PI, 0 ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 0, 0 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 5, 6, 7, 0, 0, 2*PI ); rhs->addBoneJoint( "Right", 5, 6, 7, 0, 0, 0 ); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } void testParentDoubled() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0); lhs->addBoneJoint( "Left B", 2, 2, 2, 0, 0, 0, 0); lhs->addBoneJoint( "Left AA", 3, 3, 3, 0, 0, 0, 1); rhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0); rhs->addBoneJoint( "Left B", 2, 2, 2, 0, 0, 0, 0); rhs->addBoneJoint( "Left BB", 3, 3, 3, 0, 0, 0, 2); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testParentMismatch() { // Absolute position is the same, different parent { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0); lhs->addBoneJoint( "Left C", 4, 4, 4, 0, 0, 0, 1); rhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0); rhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0); rhs->addBoneJoint( "Left C", 4, 4, 4, 0, 0, 0, 2); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Relative position is the same, different parent { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0); lhs->addBoneJoint( "Left C", 4, 4, 4, 0, 0, 0, 1); rhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0); rhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0); rhs->addBoneJoint( "Left C", 5, 5, 5, 0, 0, 0, 2); lhs->setupJoints(); rhs->setupJoints(); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testVertexInfluence() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testVertexInfluenceMissing() { // Missing Right { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Missing Left { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testVertexInfluenceMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 1, Model::IT_Custom, 1.0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 1, Model::IT_Custom, 1.0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testVertexInfluenceOffset() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 2, 0, 1 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.5 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testVertexInfluenceMultiple() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.7 ); lhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.7 ); rhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.3 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testVertexInfluenceWeighted() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.50 ); lhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.25 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.20 ); rhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.10 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testVertexInfluenceInverted() { // Invert bone joint order for 1 and 2 { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.7 ); lhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.7 ); rhs->addVertexInfluence( 1, 2, Model::IT_Custom, 0.3 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Invert influence order. { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.7 ); lhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.7 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } // Test that a weight of zero counts as no influence void testVertexInfluenceZero() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 2, 2, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.50 ); lhs->addVertexInfluence( 1, 1, Model::IT_Custom, 0.0 ); rhs->addVertex( 0, 0, 0 ); rhs->addVertex( 1, 1, 1 ); rhs->addVertex( 2, 2, 2 ); rhs->addTriangle( 0, 1, 2 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addVertexInfluence( 1, 0, Model::IT_Custom, 0.20 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testPointInfluence() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testPointInfluenceMissing() { // Missing Right { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } // Missing Left { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testPointInfluenceMismatch() { { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 1, Model::IT_Custom, 1.0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 1, Model::IT_Custom, 1.0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); QVERIFY_FALSE( lhs->equivalent( rhs.get() ) ); } } void testPointInfluenceOffset() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 1.0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.5 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testPointInfluenceMultiple() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.7 ); lhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.7 ); rhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.3 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testPointInfluenceWeighted() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.50 ); lhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.25 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.20 ); rhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.10 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } void testPointInfluenceInverted() { // Invert bone joint order for 1 and 2 { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.7 ); lhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.7 ); rhs->addPointInfluence( 1, 2, Model::IT_Custom, 0.3 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // Invert influence order. { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.7 ); lhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.3 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.7 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } } // Test that a weight of zero counts as no influence void testPointInfluenceZero() { local_ptr lhs = newTestModel(); local_ptr rhs = newTestModel(); lhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); lhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); lhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); lhs->addBoneJoint( "Left", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Left A", 2, 2, 2, 0, 0, 0, 0 ); lhs->addBoneJoint( "Left B", 3, 3, 3, 0, 0, 0, 0 ); lhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.50 ); lhs->addPointInfluence( 1, 1, Model::IT_Custom, 0.0 ); rhs->addPoint( "Point", 0, 0, 0, 0, 0, 0 ); rhs->addPoint( "Point", 1, 1, 1, 0, 0, 0 ); rhs->addPoint( "Point", 2, 2, 2, 0, 0, 0 ); rhs->addBoneJoint( "Right", 1, 1, 1, 0, 0, 0 ); rhs->addBoneJoint( "Right A", 2, 2, 2, 0, 0, 0, 0 ); rhs->addBoneJoint( "Right B", 3, 3, 3, 0, 0, 0, 0 ); rhs->addPointInfluence( 1, 0, Model::IT_Custom, 0.20 ); QVERIFY_TRUE( lhs->equivalent( rhs.get() ) ); } // FIXME // * Test influence weight mismatch // * Test animations }; // FIXME remove tolerance QTEST_MAIN(ModelEquivTest) #include "model_equiv_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_group_test.cc000066400000000000000000000305001466047437300216170ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests group methods in the Model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" class ModelGroupTest : public QObject { Q_OBJECT private: void checkGroupContents( std::list & lhs, Model * m, int grp ) { std::list rhs; if ( grp >= 0 ) rhs = m->getGroupTriangles( grp ); else rhs = m->getUngroupedTriangles(); lhs.sort(); rhs.sort(); QVERIFY_TRUE( lhs == rhs ); std::list::const_iterator it = lhs.begin(); int tcount = m->getTriangleCount(); for ( int t = 0; t < tcount; ++t ) { if ( it != lhs.end() && *it == t ) { QVERIFY_EQ( grp, m->getTriangleGroup( *it ) ); ++it; } else { QVERIFY_NE( grp, m->getTriangleGroup( t ) ); } } } void addTriangleTest( std::list & lhs, Model * m, int grp, int tri ) { m->addTriangleToGroup( grp, tri ); lhs.push_back( tri ); checkGroupContents( lhs, m, grp ); } void removeTriangleTest( std::list & lhs, Model * m, int grp, int tri ) { m->removeTriangleFromGroup( grp, tri ); lhs.remove( tri ); checkGroupContents( lhs, m, grp ); QVERIFY_NE( grp, m->getTriangleGroup( tri ) ); } void removeAll( Model * m, int grp ) { list tris = m->getGroupTriangles( grp ); while ( !tris.empty() ) { m->removeTriangleFromGroup( grp, tris.front() ); tris.pop_front(); } } private slots: void initTestCase() { log_enable_debug( false ); } // FIXME add more tests void testGroupName() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_firstname = newTestModel(); local_ptr rhs_secondname = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_firstname.get() ); rhs_list.push_back( rhs_secondname.get() ); lhs->addGroup( "Group A" ); lhs->addGroup( "First Name" ); lhs->addGroup( "Group B" ); rhs_firstname->addGroup( "Group A" ); rhs_firstname->addGroup( "First Name" ); rhs_firstname->addGroup( "Group B" ); rhs_secondname->addGroup( "Group A" ); rhs_secondname->addGroup( "Second Name" ); rhs_secondname->addGroup( "Group B" ); QVERIFY_EQ( std::string("Group A"), std::string(lhs->getGroupName(0))); QVERIFY_EQ( std::string("First Name"), std::string(lhs->getGroupName(1))); QVERIFY_EQ( std::string("Group B"), std::string(lhs->getGroupName(2))); QVERIFY_EQ( 0, lhs->getGroupByName("Group A")); QVERIFY_EQ( 1, lhs->getGroupByName("First Name")); QVERIFY_EQ( 2, lhs->getGroupByName("Group B")); QVERIFY_EQ( -1, lhs->getGroupByName("Second Name")); lhs->operationComplete( "Add groups" ); lhs->setGroupName(1, "Second Name" ); QVERIFY_EQ( std::string("Group A"), std::string(lhs->getGroupName(0))); QVERIFY_EQ( std::string("Second Name"), std::string(lhs->getGroupName(1))); QVERIFY_EQ( std::string("Group B"), std::string(lhs->getGroupName(2))); QVERIFY_EQ( 0, lhs->getGroupByName("Group A")); QVERIFY_EQ( 1, lhs->getGroupByName("Second Name")); QVERIFY_EQ( 2, lhs->getGroupByName("Group B")); QVERIFY_EQ( -1, lhs->getGroupByName("First Name")); lhs->operationComplete( "Set group name" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testGroupAngle() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_first = newTestModel(); local_ptr rhs_second = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_first.get() ); rhs_list.push_back( rhs_second.get() ); lhs->addGroup( "Group A" ); lhs->addGroup( "Group B" ); lhs->addGroup( "Group C" ); rhs_first->addGroup( "Group A" ); rhs_first->addGroup( "Group B" ); rhs_first->addGroup( "Group C" ); rhs_second->addGroup( "Group A" ); rhs_second->addGroup( "Group B" ); rhs_second->addGroup( "Group C" ); lhs->setGroupSmooth( 0, 10 ); lhs->setGroupSmooth( 1, 20 ); lhs->setGroupSmooth( 2, 30 ); rhs_first->setGroupSmooth( 0, 10 ); rhs_first->setGroupSmooth( 1, 20 ); rhs_first->setGroupSmooth( 2, 30 ); rhs_second->setGroupSmooth( 0, 10 ); rhs_second->setGroupSmooth( 1, 80 ); rhs_second->setGroupSmooth( 2, 30 ); QVERIFY_EQ( (uint8_t) 10, lhs->getGroupSmooth(0)); QVERIFY_EQ( (uint8_t) 20, lhs->getGroupSmooth(1)); QVERIFY_EQ( (uint8_t) 30, lhs->getGroupSmooth(2)); lhs->operationComplete( "Add groups" ); lhs->setGroupSmooth(1, 80 ); QVERIFY_EQ( (uint8_t) 10, lhs->getGroupSmooth(0)); QVERIFY_EQ( (uint8_t) 80, lhs->getGroupSmooth(1)); QVERIFY_EQ( (uint8_t) 30, lhs->getGroupSmooth(2)); lhs->operationComplete( "Set group smoothness" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testAddRemove() { local_ptr m = loadModelOrDie( "data/model_hidden_test.mm3d" ); m->unhideAll(); int grp = m->addGroup( "New Group" ); std::list lhs; // Should be empty QVERIFY( lhs == m->getGroupTriangles( grp ) ); addTriangleTest( lhs, m.get(), grp, 0 ); addTriangleTest( lhs, m.get(), grp, 1 ); addTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 1 ); removeTriangleTest( lhs, m.get(), grp, 0 ); addTriangleTest( lhs, m.get(), grp, 0 ); addTriangleTest( lhs, m.get(), grp, 1 ); addTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 0 ); removeTriangleTest( lhs, m.get(), grp, 1 ); removeTriangleTest( lhs, m.get(), grp, 2 ); addTriangleTest( lhs, m.get(), grp, 2 ); addTriangleTest( lhs, m.get(), grp, 1 ); addTriangleTest( lhs, m.get(), grp, 0 ); removeTriangleTest( lhs, m.get(), grp, 0 ); removeTriangleTest( lhs, m.get(), grp, 1 ); removeTriangleTest( lhs, m.get(), grp, 2 ); addTriangleTest( lhs, m.get(), grp, 2 ); addTriangleTest( lhs, m.get(), grp, 1 ); addTriangleTest( lhs, m.get(), grp, 0 ); removeTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 1 ); removeTriangleTest( lhs, m.get(), grp, 0 ); addTriangleTest( lhs, m.get(), grp, 0 ); addTriangleTest( lhs, m.get(), grp, 1 ); addTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 1 ); removeTriangleTest( lhs, m.get(), grp, 0 ); removeTriangleTest( lhs, m.get(), grp, 2 ); addTriangleTest( lhs, m.get(), grp, 0 ); addTriangleTest( lhs, m.get(), grp, 1 ); addTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 1 ); removeTriangleTest( lhs, m.get(), grp, 2 ); removeTriangleTest( lhs, m.get(), grp, 0 ); } void testAddSelected() { local_ptr m = newTestModel(); for ( int t = 0; t < 12; ++t ) { m->addVertex( (double) t, (double) t + 1, (double) t - 1 ); } for ( int t = 0; t < 10; ++t ) { m->addTriangle( t, t + 1, t + 2 ); } int grp = m->addGroup( "New Group" ); std::list lhs; // Should be empty QVERIFY( lhs == m->getGroupTriangles( grp ) ); lhs.clear(); lhs.push_back(0); lhs.push_back(1); lhs.push_back(2); lhs.push_back(3); lhs.push_back(4); lhs.push_back(5); lhs.push_back(6); lhs.push_back(7); lhs.push_back(8); lhs.push_back(9); checkGroupContents( lhs, m.get(), -1 ); // Test addSelectedToGroup() m->selectTriangle( 0 ); m->selectTriangle( 1 ); m->selectTriangle( 2 ); m->selectTriangle( 3 ); lhs.clear(); lhs.push_back( 0 ); lhs.push_back( 1 ); lhs.push_back( 2 ); lhs.push_back( 3 ); m->addSelectedToGroup( grp ); checkGroupContents( lhs, m.get(), grp ); lhs.clear(); lhs.push_back(4); lhs.push_back(5); lhs.push_back(6); lhs.push_back(7); lhs.push_back(8); lhs.push_back(9); checkGroupContents( lhs, m.get(), -1 ); m->unselectAll(); // Test addSelectedToGroup() does not duplicate triangles in group m->selectTriangle( 0 ); m->selectTriangle( 3 ); m->selectTriangle( 4 ); m->selectTriangle( 5 ); lhs.clear(); lhs.push_back( 0 ); lhs.push_back( 1 ); lhs.push_back( 2 ); lhs.push_back( 3 ); lhs.push_back( 4 ); lhs.push_back( 5 ); m->addSelectedToGroup( grp ); checkGroupContents( lhs, m.get(), grp ); lhs.clear(); lhs.push_back(6); lhs.push_back(7); lhs.push_back(8); lhs.push_back(9); checkGroupContents( lhs, m.get(), -1 ); m->unselectAll(); // Test setSelectedAsGroup(), including removal of triangles that // are not in the new set and inclusion of triangles that were // in the group originally. m->selectTriangle( 1 ); m->selectTriangle( 3 ); m->selectTriangle( 5 ); m->selectTriangle( 7 ); m->selectTriangle( 9 ); lhs.clear(); lhs.push_back( 1 ); lhs.push_back( 3 ); lhs.push_back( 5 ); lhs.push_back( 7 ); lhs.push_back( 9 ); m->setSelectedAsGroup( grp ); checkGroupContents( lhs, m.get(), grp ); lhs.clear(); lhs.push_back(0); lhs.push_back(2); lhs.push_back(4); lhs.push_back(6); lhs.push_back(8); checkGroupContents( lhs, m.get(), -1 ); m->unselectAll(); // Test that addSelectedToGroup removes triangles from another group int grp2 = m->addGroup( "New Group 2" ); m->selectTriangle( 3 ); m->selectTriangle( 5 ); m->addSelectedToGroup( grp2 ); lhs.clear(); lhs.push_back( 1 ); lhs.push_back( 7 ); lhs.push_back( 9 ); checkGroupContents( lhs, m.get(), grp ); lhs.clear(); lhs.push_back( 3 ); lhs.push_back( 5 ); checkGroupContents( lhs, m.get(), grp2 ); lhs.clear(); lhs.push_back(0); lhs.push_back(2); lhs.push_back(4); lhs.push_back(6); lhs.push_back(8); checkGroupContents( lhs, m.get(), -1 ); m->unselectAll(); // Test that setSelecedAsGroup removes triangles from another group m->selectTriangle( 1 ); m->setSelectedAsGroup( grp2 ); lhs.clear(); lhs.push_back( 1 ); checkGroupContents( lhs, m.get(), grp2 ); lhs.clear(); lhs.push_back( 7 ); lhs.push_back( 9 ); checkGroupContents( lhs, m.get(), grp ); lhs.clear(); lhs.push_back(0); lhs.push_back(2); lhs.push_back(3); lhs.push_back(4); lhs.push_back(5); lhs.push_back(6); lhs.push_back(8); checkGroupContents( lhs, m.get(), -1 ); m->unselectAll(); // FIXME test undo } // FIXME add tests: // deletion preserves triangle indices in group membership set // normal blending // smoothness // max angle // undo }; QTEST_MAIN(ModelGroupTest) #include "model_group_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_init_test.cc000066400000000000000000000155201466047437300214330ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests initialization of the model compenents (inner classes). #include #include "test_common.h" #include "model.h" #include "texture.h" #include "local_array.h" class ModelInitTest : public QObject { Q_OBJECT private: void vertexIsInitialized( Model::Vertex * v ) { QVERIFY_TRUE( v->m_visible ); QVERIFY_FALSE( v->m_selected ); QVERIFY_FALSE( v->m_free ); QVERIFY( v->m_drawSource == v->m_coord ); } void triangleIsInitialized( Model::Triangle * t ) { QVERIFY_TRUE( t->m_visible ); QVERIFY_FALSE( t->m_selected ); QVERIFY_EQ( -1, t->m_projection ); // FIXME need fuzzy EQ for floats QVERIFY( t->m_flatSource == t->m_flatNormals ); QVERIFY( t->m_normalSource[0] == t->m_finalNormals[0] ); QVERIFY( t->m_normalSource[1] == t->m_finalNormals[1] ); QVERIFY( t->m_normalSource[2] == t->m_finalNormals[2] ); } void groupIsInitialized( Model::Group * g ) { QVERIFY_EQ( std::string(""), g->m_name ); QVERIFY_EQ( -1, g->m_materialIndex ); QVERIFY_EQ( (size_t) 0, g->m_triangleIndices.size() ); QVERIFY_EQ( (uint8_t) 255, g->m_smooth ); QVERIFY_EQ( (uint8_t) 89, g->m_angle ); QVERIFY_TRUE( g->m_visible ); QVERIFY_FALSE( g->m_selected ); } void materialIsInitialized( Model::Material * m ) { QVERIFY_EQ( std::string(""), m->m_name ); QVERIFY_EQ( Model::Material::MATTYPE_TEXTURE, m->m_type ); // FIXME need fuzzy EQ for floats QVERIFY_FALSE( m->m_sClamp ); QVERIFY_FALSE( m->m_tClamp ); QVERIFY_EQ( (GLuint) 0, m->m_texture ); QVERIFY_EQ( std::string(""), m->m_filename ); QVERIFY_EQ( std::string(""), m->m_alphaFilename ); QVERIFY_TRUE( NULL == m->m_textureData ); } private slots: // Many primitives are recycled. Test initial conditions, change conditions, // release, and re-get to make sure that the recyled primitives are properly // initialized. void testVertexInit() { Model::Vertex * v = Model::Vertex::get(); QVERIFY_EQ(1, Model::Vertex::allocated() ); QVERIFY_EQ(0, Model::Vertex::recycled() ); vertexIsInitialized( v ); v->m_visible = false; v->m_selected = true; v->m_free = true; v->m_drawSource = v->m_kfCoord; v->release(); QVERIFY_EQ(1, Model::Vertex::allocated() ); QVERIFY_EQ(1, Model::Vertex::recycled() ); v = Model::Vertex::get(); vertexIsInitialized( v ); QVERIFY_EQ(1, Model::Vertex::allocated() ); QVERIFY_EQ(0, Model::Vertex::recycled() ); v->release(); QVERIFY_EQ(1, Model::Vertex::allocated() ); QVERIFY_EQ(1, Model::Vertex::recycled() ); QVERIFY_EQ(1, Model::Vertex::flush() ) QVERIFY_EQ(0, Model::Vertex::allocated() ); QVERIFY_EQ(0, Model::Vertex::recycled() ); } void testTriangleInit() { Model::Triangle * t = Model::Triangle::get(); QVERIFY_EQ(1, Model::Triangle::allocated() ); QVERIFY_EQ(0, Model::Triangle::recycled() ); triangleIsInitialized( t ); t->m_visible = false; t->m_selected = true; t->m_projection = 7; t->m_flatSource = t->m_flatNormals; t->m_normalSource[0] = t->m_finalNormals[0]; t->m_normalSource[1] = t->m_finalNormals[1]; t->m_normalSource[2] = t->m_finalNormals[2]; t->release(); QVERIFY_EQ(1, Model::Triangle::allocated() ); QVERIFY_EQ(1, Model::Triangle::recycled() ); t = Model::Triangle::get(); triangleIsInitialized( t ); QVERIFY_EQ(1, Model::Triangle::allocated() ); QVERIFY_EQ(0, Model::Triangle::recycled() ); t->release(); QVERIFY_EQ(1, Model::Triangle::allocated() ); QVERIFY_EQ(1, Model::Triangle::recycled() ); QVERIFY_EQ(1, Model::Triangle::flush() ) QVERIFY_EQ(0, Model::Triangle::allocated() ); QVERIFY_EQ(0, Model::Triangle::recycled() ); } void testGroupInit() { Model::Group * g = Model::Group::get(); QVERIFY_EQ(1, Model::Group::allocated() ); QVERIFY_EQ(0, Model::Group::recycled() ); groupIsInitialized( g ); g->m_name = "dummy name"; g->m_materialIndex = 7; g->m_triangleIndices.insert(0); g->m_smooth = 8; g->m_angle = 9; g->m_visible = false; g->m_selected = true; g->release(); QVERIFY_EQ(1, Model::Group::allocated() ); QVERIFY_EQ(1, Model::Group::recycled() ); g = Model::Group::get(); groupIsInitialized( g ); QVERIFY_EQ(1, Model::Group::allocated() ); QVERIFY_EQ(0, Model::Group::recycled() ); g->release(); QVERIFY_EQ(1, Model::Group::allocated() ); QVERIFY_EQ(1, Model::Group::recycled() ); QVERIFY_EQ(1, Model::Group::flush() ) QVERIFY_EQ(0, Model::Group::allocated() ); QVERIFY_EQ(0, Model::Group::recycled() ); } void testMaterialInit() { Model::Material * m = Model::Material::get(); QVERIFY_EQ(1, Model::Material::allocated() ); QVERIFY_EQ(0, Model::Material::recycled() ); materialIsInitialized( m ); m->m_name = "dummy name"; m->m_type = Model::Material::MATTYPE_BLANK; // FIXME need fuzzy EQ for floats m->m_sClamp = true; m->m_tClamp = true; m->m_texture = 3; m->m_filename = "dummy file"; m->m_alphaFilename = "dummy alpha"; m->m_textureData = (Texture *) 100; m->release(); QVERIFY_EQ(1, Model::Material::allocated() ); QVERIFY_EQ(1, Model::Material::recycled() ); m = Model::Material::get(); materialIsInitialized( m ); QVERIFY_EQ(1, Model::Material::allocated() ); QVERIFY_EQ(0, Model::Material::recycled() ); m->release(); QVERIFY_EQ(1, Model::Material::allocated() ); QVERIFY_EQ(1, Model::Material::recycled() ); QVERIFY_EQ(1, Model::Material::flush() ) QVERIFY_EQ(0, Model::Material::allocated() ); QVERIFY_EQ(0, Model::Material::recycled() ); } // FIXME test other inits }; QTEST_MAIN(ModelInitTest) #include "model_init_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_joint_test.cc000066400000000000000000000257221466047437300216200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests bone joint methods in the Model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "log.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" #include class ModelJointTest : public QObject { Q_OBJECT private: private slots: void initTestCase() { log_enable_debug( false ); } void testAddBoneJoint() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_1->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); rhs_2->addBoneJoint( "Joint 4", 4, 4, 4, 0, 0, 1, 0 ); QVERIFY_EQ( 0, (int) lhs->getBoneJointCount() ); lhs->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); QVERIFY_EQ( 2, (int) lhs->getBoneJointCount() ); QVERIFY_EQ( std::string("Joint 1"), std::string(lhs->getBoneJointName(0)) ); QVERIFY_EQ( std::string("Joint 2"), std::string(lhs->getBoneJointName(1)) ); lhs->operationComplete( "Add joints 1 and 2" ); lhs->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); lhs->addBoneJoint( "Joint 4", 4, 4, 4, 0, 0, 1, 0 ); QVERIFY_EQ( 4, (int) lhs->getBoneJointCount() ); QVERIFY_EQ( std::string("Joint 1"), std::string(lhs->getBoneJointName(0)) ); QVERIFY_EQ( std::string("Joint 2"), std::string(lhs->getBoneJointName(1)) ); QVERIFY_EQ( std::string("Joint 3"), std::string(lhs->getBoneJointName(2)) ); QVERIFY_EQ( std::string("Joint 4"), std::string(lhs->getBoneJointName(3)) ); lhs->operationComplete( "Add joints 3 and 4" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testDeleteBoneJoint() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_1->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); rhs_2->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); // FIXME: This doesn't account for adding bone with rotated parent applying // counter rotation or deleting bone moving translation. rhs_2->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 0 ); QVERIFY_EQ( 0, (int) lhs->getBoneJointCount() ); lhs->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); QVERIFY_EQ( 3, (int) lhs->getBoneJointCount() ); QVERIFY_EQ( 1, (int) lhs->getBoneJointParent(2) ); QVERIFY_EQ( std::string("Joint 1"), std::string(lhs->getBoneJointName(0)) ); QVERIFY_EQ( std::string("Joint 2"), std::string(lhs->getBoneJointName(1)) ); QVERIFY_EQ( std::string("Joint 3"), std::string(lhs->getBoneJointName(2)) ); lhs->operationComplete( "Add joints" ); lhs->deleteBoneJoint( 1 ); QVERIFY_EQ( 2, (int) lhs->getBoneJointCount() ); QVERIFY_EQ( 0, (int) lhs->getBoneJointParent(1) ); QVERIFY_EQ( std::string("Joint 1"), std::string(lhs->getBoneJointName(0)) ); QVERIFY_EQ( std::string("Joint 3"), std::string(lhs->getBoneJointName(1)) ); lhs->operationComplete( "Delete joint" ); #warning checkUndoRedo() for testDeleteBoneJoint() is broken #if 0 // See FIXME above checkUndoRedo( 2, lhs.get(), rhs_list ); #endif } void testRenameBoneJoint() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_1->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); rhs_2->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Renamed", 2, 2, 2, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); QVERIFY_EQ( 0, (int) lhs->getBoneJointCount() ); lhs->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); QVERIFY_EQ( 3, (int) lhs->getBoneJointCount() ); QVERIFY_EQ( std::string("Joint 1"), std::string(lhs->getBoneJointName(0)) ); QVERIFY_EQ( std::string("Joint 2"), std::string(lhs->getBoneJointName(1)) ); QVERIFY_EQ( std::string("Joint 3"), std::string(lhs->getBoneJointName(2)) ); lhs->operationComplete( "Add joints 1 and 2" ); lhs->setBoneJointName( 1, "Renamed" ); QVERIFY_EQ( std::string("Joint 1"), std::string(lhs->getBoneJointName(0)) ); QVERIFY_EQ( std::string("Renamed"), std::string(lhs->getBoneJointName(1)) ); QVERIFY_EQ( std::string("Joint 3"), std::string(lhs->getBoneJointName(2)) ); lhs->operationComplete( "Renamed bone joint" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testMoveBoneJoint() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_1->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); rhs_1->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); rhs_2->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 2", 4, 5, 6, 1, 0, 0, 0 ); rhs_2->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); lhs->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 2", 2, 2, 2, 1, 0, 0, 0 ); lhs->addBoneJoint( "Joint 3", 3, 3, 3, 0, 1, 0, 1 ); double expected[3] = { 0, 0, 0 }; double actual[3] = { 0, 0, 0 }; expected[0] = 1; expected[1] = 1; expected[2] = 1; lhs->getBoneJointCoords( 0, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 2; expected[1] = 2; expected[2] = 2; lhs->getBoneJointCoords( 1, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 3; expected[1] = 3; expected[2] = 3; lhs->getBoneJointCoords( 2, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); lhs->operationComplete( "Add joints 1 and 2" ); lhs->moveBoneJoint( 1, 4, 5, 6 ); expected[0] = 1; expected[1] = 1; expected[2] = 1; lhs->getBoneJointCoords( 0, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 4; expected[1] = 5; expected[2] = 6; lhs->getBoneJointCoords( 1, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 3; expected[1] = 3; expected[2] = 3; lhs->getBoneJointCoords( 2, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); lhs->operationComplete( "Move bone joint" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testGetLocalTranslation() { { local_ptr lhs = newTestModel(); lhs->addBoneJoint( "Joint 1", 1, 1, 1, 0, 0, 0 ); lhs->setupJoints(); const double trans[3] = { 1, 2, 3 }; lhs->setBoneJointTranslation( 0, trans ); lhs->setupJoints(); const double expected[3] = { 1, 2, 3 }; double actual[3] = { 0, 0, 0 }; lhs->getBoneJointCoords( 0, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); } { local_ptr lhs = newTestModel(); lhs->addBoneJoint( "Joint 1", 1, 2, 3, 0, 0, 0 ); lhs->addBoneJoint( "Joint 1", 2, 2, 2, 0, 0, 0, 0 ); lhs->setupJoints(); const double trans[3] = { 4, 3, 2 }; lhs->setBoneJointTranslation( 1, trans ); lhs->setupJoints(); const double expected[3] = { 5, 5, 5 }; double actual[3] = { 0, 0, 0 }; lhs->getBoneJointCoords( 1, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); } // FIXME rotation // FIXME multiple levels of joints // FIXME undo } // FIXME Tests to add: // x add joint // x delete joint // x rename joint // x get/set joint coords // x moveBoneJoint // x get joint parent // selection (parent joint selected) // hide/unhide // joint matrix is correct // Final // Absolute // Relative // - setBoneJointTranslation // setBoneJointRotation // get/setVertexBoneJoint // get/setPointBoneJoint // getBoneJointVertices // // FIXME Tests to add (in other files): // add/removeInfluence // Single // Multiple // Too many // getInfluenceList // getPrimaryInfluence // get/setInfluenceType // get/setInfluenceWeight // autoSetInfluences // calcRemainderWeight // Calculate weight // // FIXME Tests to add (in other files): // Skeletal animation // Setting keyframes directly // Modifying keyframes with transforms // Weight has proper effect with single joint // Weight has proper effect with multiple joints // Deleting a bone joint adjust influence indices }; QTEST_MAIN(ModelJointTest) #include "model_joint_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_material_test.cc000066400000000000000000000531131466047437300222660ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests grouping methods in the Model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "tgatex.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" // FIXME centralize this Texture * loadTextureOrDie( TextureFilter * f, const char * filename ) { Texture * tex = new Texture; Texture::ErrorE err = f->readFile( tex, filename ); if ( err != Texture::ERROR_NONE ) { fprintf( stderr, "fatal: %s: %s\n", filename, Texture::errorToString( err ) ); delete tex; exit( -1 ); } return tex; } Texture * loadTgaOrDie( const char * filename ) { TgaTextureFilter f; return loadTextureOrDie( &f, filename ); } class ModelMaterialTest : public QObject { Q_OBJECT private: private slots: void initTestCase() { log_enable_debug( false ); } // FIXME add more tests void testAddColorMaterial() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); QVERIFY_EQ( 0, lhs->getTextureCount() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Renamed" ); rhs_2->addColorMaterial( "Three" ); QVERIFY_EQ( 3, lhs->getTextureCount() ); QVERIFY_EQ( (int) Model::Material::MATTYPE_BLANK, (int) lhs->getMaterialType(1) ); QVERIFY_TRUE( NULL == lhs->getTextureData(1) ); QVERIFY_EQ( std::string("One"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Two"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("Three"), std::string(lhs->getTextureName(2))); QVERIFY_EQ( 0, lhs->getMaterialByName("One")); QVERIFY_EQ( 1, lhs->getMaterialByName("Two")); QVERIFY_EQ( 2, lhs->getMaterialByName("Three")); QVERIFY_EQ( -1, lhs->getMaterialByName("Renamed")); lhs->operationComplete( "Add materials" ); lhs->setTextureName(1, "Renamed" ); QVERIFY_EQ( std::string("One"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Renamed"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("Three"), std::string(lhs->getTextureName(2))); QVERIFY_EQ( 0, lhs->getMaterialByName("One")); QVERIFY_EQ( 1, lhs->getMaterialByName("Renamed")); QVERIFY_EQ( 2, lhs->getMaterialByName("Three")); QVERIFY_EQ( -1, lhs->getMaterialByName("Two")); lhs->operationComplete( "Set material name" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testAddTextureMaterial() { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr tex2 = loadTgaOrDie( "data/test_rgb_uncomp.tga" ); local_ptr tex3 = loadTgaOrDie( "data/test_rgba_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); QVERIFY_EQ( 0, lhs->getTextureCount() ); lhs->addTexture( tex1.get() ); lhs->addTexture( tex2.get() ); lhs->addTexture( tex3.get() ); rhs_1->addTexture( tex1.get() ); rhs_1->addTexture( tex2.get() ); rhs_1->addTexture( tex3.get() ); rhs_2->addTexture( tex1.get() ); rhs_2->addTexture( tex2.get() ); rhs_2->addTexture( tex3.get() ); rhs_2->setTextureName( 1, "Renamed" ); QVERIFY_EQ( 3, lhs->getTextureCount() ); QVERIFY_EQ( (int) Model::Material::MATTYPE_TEXTURE, (int) lhs->getMaterialType(1) ); QVERIFY_TRUE( tex2.get() == lhs->getTextureData(1) ); QVERIFY_EQ( std::string("data/test_rgb_uncomp.tga"), std::string(lhs->getTextureFilename(1)) ); QVERIFY_EQ( std::string("test_rgb_comp"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("test_rgb_uncomp"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("test_rgba_comp"), std::string(lhs->getTextureName(2))); QVERIFY_EQ( 0, lhs->getMaterialByName("test_rgb_comp")); QVERIFY_EQ( 1, lhs->getMaterialByName("test_rgb_uncomp")); QVERIFY_EQ( 2, lhs->getMaterialByName("test_rgba_comp")); QVERIFY_EQ( -1, lhs->getMaterialByName("Renamed")); lhs->operationComplete( "Add materials" ); lhs->setTextureName(1, "Renamed" ); QVERIFY_EQ( std::string("test_rgb_comp"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Renamed"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("test_rgba_comp"), std::string(lhs->getTextureName(2))); QVERIFY_EQ( 0, lhs->getMaterialByName("test_rgb_comp")); QVERIFY_EQ( 1, lhs->getMaterialByName("Renamed")); QVERIFY_EQ( 2, lhs->getMaterialByName("test_rgba_comp")); QVERIFY_EQ( -1, lhs->getMaterialByName("test_rgb_uncomp")); lhs->operationComplete( "Set material name" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testDeleteMaterial() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); QVERIFY_EQ( 0, lhs->getTextureCount() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); lhs->addGroup( "One" ); lhs->addGroup( "Two" ); lhs->addGroup( "Three" ); lhs->setGroupTextureId( 0, 2 ); lhs->setGroupTextureId( 1, 1 ); lhs->setGroupTextureId( 2, 0 ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_1->addGroup( "One" ); rhs_1->addGroup( "Two" ); rhs_1->addGroup( "Three" ); rhs_1->setGroupTextureId( 0, 2 ); rhs_1->setGroupTextureId( 1, 1 ); rhs_1->setGroupTextureId( 2, 0 ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Three" ); rhs_2->addGroup( "One" ); rhs_2->addGroup( "Two" ); rhs_2->addGroup( "Three" ); rhs_2->setGroupTextureId( 0, 1 ); rhs_2->setGroupTextureId( 2, 0 ); QVERIFY_EQ( 3, lhs->getTextureCount() ); lhs->operationComplete( "Add materials and groups" ); lhs->deleteTexture( 1 ); QVERIFY_EQ( 2, lhs->getTextureCount() ); QVERIFY_EQ( 1, lhs->getGroupTextureId(0)); QVERIFY_EQ( -1, lhs->getGroupTextureId(1)); QVERIFY_EQ( 0, lhs->getGroupTextureId(2)); QVERIFY_EQ( std::string("One"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Three"), std::string(lhs->getTextureName(1))); lhs->operationComplete( "Delete material" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testAddRemoveTexture() { local_ptr tex1 = loadTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_1.get() ); QVERIFY_EQ( 0, lhs->getTextureCount() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); rhs_2->setMaterialTexture( 1, tex1.get() ); QVERIFY_EQ( std::string("One"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Two"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("Three"), std::string(lhs->getTextureName(2))); QVERIFY_EQ( (int) Model::Material::MATTYPE_BLANK, (int) lhs->getMaterialType(1) ); QVERIFY_TRUE( NULL == lhs->getTextureData(1) ); lhs->operationComplete( "Add materials" ); lhs->setMaterialTexture(1, tex1.get() ); QVERIFY_TRUE( tex1.get() == lhs->getTextureData(1) ); QVERIFY_EQ( std::string("data/test_rgb_comp.tga"), std::string(lhs->getTextureFilename(1)) ); QVERIFY_EQ( (int) Model::Material::MATTYPE_TEXTURE, (int) lhs->getMaterialType(1) ); QVERIFY_EQ( std::string("One"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Two"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("Three"), std::string(lhs->getTextureName(2))); lhs->operationComplete( "Set material texture" ); lhs->removeMaterialTexture( 1 ); QVERIFY_EQ( (int) Model::Material::MATTYPE_BLANK, (int) lhs->getMaterialType(1) ); QVERIFY_TRUE( NULL == lhs->getTextureData(1) ); QVERIFY_EQ( std::string("One"), std::string(lhs->getTextureName(0))); QVERIFY_EQ( std::string("Two"), std::string(lhs->getTextureName(1))); QVERIFY_EQ( std::string("Three"), std::string(lhs->getTextureName(2))); lhs->operationComplete( "Set material texture" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } void testSetGroupMaterial() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); QVERIFY_EQ( 0, lhs->getTextureCount() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); lhs->addGroup( "One" ); lhs->addGroup( "Two" ); lhs->addGroup( "Three" ); lhs->setGroupTextureId( 0, 0 ); lhs->setGroupTextureId( 1, 1 ); lhs->setGroupTextureId( 2, 2 ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_1->addGroup( "One" ); rhs_1->addGroup( "Two" ); rhs_1->addGroup( "Three" ); rhs_1->setGroupTextureId( 0, 0 ); rhs_1->setGroupTextureId( 1, 1 ); rhs_1->setGroupTextureId( 2, 2 ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); rhs_2->addGroup( "One" ); rhs_2->addGroup( "Two" ); rhs_2->addGroup( "Three" ); rhs_2->setGroupTextureId( 0, 2 ); rhs_2->setGroupTextureId( 2, 0 ); QVERIFY_EQ( 0, lhs->getGroupTextureId(0)); QVERIFY_EQ( 1, lhs->getGroupTextureId(1)); QVERIFY_EQ( 2, lhs->getGroupTextureId(2)); lhs->operationComplete( "Add materials and groups" ); lhs->setGroupTextureId( 0, 2 ); lhs->setGroupTextureId( 1, -1 ); lhs->setGroupTextureId( 2, 0 ); QVERIFY_EQ( 2, lhs->getGroupTextureId(0)); QVERIFY_EQ( -1, lhs->getGroupTextureId(1)); QVERIFY_EQ( 0, lhs->getGroupTextureId(2)); lhs->operationComplete( "Set group material ID" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetClamp() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); local_ptr rhs_3 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); rhs_list.push_back( rhs_3.get() ); QVERIFY_EQ( 0, lhs->getTextureCount() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); rhs_3->addColorMaterial( "One" ); rhs_3->addColorMaterial( "Two" ); rhs_3->addColorMaterial( "Three" ); lhs->setTextureSClamp( 1, false ); rhs_1->setTextureSClamp( 1, false ); rhs_2->setTextureSClamp( 1, true ); rhs_3->setTextureSClamp( 1, true ); lhs->setTextureTClamp( 1, false ); rhs_1->setTextureTClamp( 1, false ); rhs_2->setTextureTClamp( 1, false ); rhs_3->setTextureTClamp( 1, true ); QVERIFY_FALSE( lhs->getTextureSClamp(1) ); QVERIFY_FALSE( lhs->getTextureTClamp(1) ); lhs->operationComplete( "Add materials" ); lhs->setTextureSClamp( 1, true ); QVERIFY_TRUE( lhs->getTextureSClamp(1) ); QVERIFY_FALSE( lhs->getTextureTClamp(1) ); lhs->operationComplete( "Set material S clamp" ); lhs->setTextureTClamp( 1, true ); QVERIFY_TRUE( lhs->getTextureSClamp(1) ); QVERIFY_TRUE( lhs->getTextureTClamp(1) ); lhs->operationComplete( "Set material T clamp" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } void testSetAmbient() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); const float orig[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; const float change[4] = { 0.2f, 0.4f, 0.6f, 0.8f }; float actual[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; lhs->setTextureAmbient( 1, orig ); rhs_1->setTextureAmbient( 1, orig ); rhs_2->setTextureAmbient( 1, change ); lhs->getTextureAmbient( 1, actual ); QVERIFY_ARRAY_EQ( orig, 4, actual, 4 ); lhs->operationComplete( "Add materials" ); lhs->setTextureAmbient( 1, change ); lhs->getTextureAmbient( 1, actual ); QVERIFY_ARRAY_EQ( change, 4, actual, 4 ); lhs->operationComplete( "Set material ambient" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetDiffuse() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); const float orig[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; const float change[4] = { 0.2f, 0.4f, 0.6f, 0.8f }; float actual[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; lhs->setTextureDiffuse( 1, orig ); rhs_1->setTextureDiffuse( 1, orig ); rhs_2->setTextureDiffuse( 1, change ); lhs->getTextureDiffuse( 1, actual ); QVERIFY_ARRAY_EQ( orig, 4, actual, 4 ); lhs->operationComplete( "Add materials" ); lhs->setTextureDiffuse( 1, change ); lhs->getTextureDiffuse( 1, actual ); QVERIFY_ARRAY_EQ( change, 4, actual, 4 ); lhs->operationComplete( "Set material diffuse" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetEmissive() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); const float orig[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; const float change[4] = { 0.2f, 0.4f, 0.6f, 0.8f }; float actual[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; lhs->setTextureEmissive( 1, orig ); rhs_1->setTextureEmissive( 1, orig ); rhs_2->setTextureEmissive( 1, change ); lhs->getTextureEmissive( 1, actual ); QVERIFY_ARRAY_EQ( orig, 4, actual, 4 ); lhs->operationComplete( "Add materials" ); lhs->setTextureEmissive( 1, change ); lhs->getTextureEmissive( 1, actual ); QVERIFY_ARRAY_EQ( change, 4, actual, 4 ); lhs->operationComplete( "Set material emissive" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetSpecular() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); const float orig[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; const float change[4] = { 0.2f, 0.4f, 0.6f, 0.8f }; float actual[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; lhs->setTextureSpecular( 1, orig ); rhs_1->setTextureSpecular( 1, orig ); rhs_2->setTextureSpecular( 1, change ); lhs->getTextureSpecular( 1, actual ); QVERIFY_ARRAY_EQ( orig, 4, actual, 4 ); lhs->operationComplete( "Add materials" ); lhs->setTextureSpecular( 1, change ); lhs->getTextureSpecular( 1, actual ); QVERIFY_ARRAY_EQ( change, 4, actual, 4 ); lhs->operationComplete( "Set material specular" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetShininess() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_1 = newTestModel(); local_ptr rhs_2 = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_1.get() ); rhs_list.push_back( rhs_2.get() ); lhs->addColorMaterial( "One" ); lhs->addColorMaterial( "Two" ); lhs->addColorMaterial( "Three" ); rhs_1->addColorMaterial( "One" ); rhs_1->addColorMaterial( "Two" ); rhs_1->addColorMaterial( "Three" ); rhs_2->addColorMaterial( "One" ); rhs_2->addColorMaterial( "Two" ); rhs_2->addColorMaterial( "Three" ); const float orig = 1.0f; const float change = 0.5f; float actual = 1.0f; lhs->setTextureShininess( 1, orig ); rhs_1->setTextureShininess( 1, orig ); rhs_2->setTextureShininess( 1, change ); lhs->getTextureShininess( 1, actual ); QVERIFY_EQ( orig, actual ); lhs->operationComplete( "Add materials" ); lhs->setTextureShininess( 1, change ); lhs->getTextureShininess( 1, actual ); QVERIFY_EQ( change, actual ); lhs->operationComplete( "Set material shininess" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } // FIXME in another file: // rendering // type // lighting // clamp // load textures // invalidate textures }; QTEST_MAIN(ModelMaterialTest) #include "model_material_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_select_test.cc000066400000000000000000000033141466047437300217450ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests selection methods in the Model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" class ModelSelectTest : public QObject { Q_OBJECT private: private slots: void initTestCase() { log_enable_debug( false ); } // FIXME add some real tests void testConnectedWithHidden() { local_ptr m = loadModelOrDie( "data/model_hidden_test.mm3d" ); m->setSelectionMode( Model::SelectConnected ); Matrix mat; mat.loadIdentity(); m->selectInVolumeMatrix( mat, -1.0, -1.0, 1.0, 1.0 ); QVERIFY_EQ( 36, (int) m->getSelectedTriangleCount() ); } }; QTEST_MAIN(ModelSelectTest) #include "model_select_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_test.cc000066400000000000000000000077101466047437300204120ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests the primary functionality of libmm3d/model.h // Some features of the Model class are tested by other test files. // TODO General: // * Observer add/remove/receive changes // * Error code to failure mapping // * Push/pop errors // * diff (equivalent) // * get/set filename // * get/set export file // * get/set filter-specific error // * drawing (right...) // * texture/context loading/removing, deleteGLTextures // * Invalidate textures // * Texture data compare fuzziness // * add/delete/get format data // * background images get/set image/scale/center // * meta data get(by name/index)/set/removeLast(internal)/clear // * merge models // * merge animations // * boolean operations // * render options (joint mode, projections, canvas draw mode) // // TODO Geometry: // * Deletions // - Delete selected // - Force add/delete // x Remove oprhaned vertices (and not free vertices) // x Remove flattened triangles // * Set/get primitive properties // x Vertices // x Faces // x Groups // x Materials // - Bone Joints // - Points // - Texture Projections (seam/up/range/scale/type/rotation(misnamed arg)) // - Moving // - Hiding // x Hide selected // - Hide unselected // x Unhide all // * Selection // - Volume selection // - Selection mode // - Interaction with visibility // - Selection test // - Invert selection // - Selection difference // - Joint parent selection // - Select primitives from other primitives // - getSelected{PRIMITIVE} list // * Bounding region // * Transforms // - Translate // - Rotate // - Apply Matrix (w/undoable or not) // * Subdivide/unsubdivide // * Simplify mesh // * Normals/cosToPoint // * Anim Normals // * BSP Tree generation // * Grouping and group properties // * Skeletal structure // * Bone joint matrices // * Bone joint assignment // * Bone joint weighting calculations // * Bone primary influence calculations // * Auto assign bone joint // * getBoneVector // * Position accessors // * Texture coordinates // * Projection mapping // * Local matrix // // TODO Undo: // * Undo works on everything // * Redo works on everything // x Undo current // * Enable/disable works // x Size calculation works // x Limits work // x Atomic operation names // x canUndo/canRedo // * setUndoEnabled // x Save interaction (getSaved/setSaved) // x undoRelease/redoRelease // // TODO Animation: // * Add/remove animation // * Properties (name, fps, etc.) // * Add/remove animation frames // * Copy/join/split/merge/convert // * Move animation // * Setting animation time/frame/none // * Looping // * Set/get animation frame data // - Keyframe // - Mesh deformation // - Clear // * Interpolation // #include #include "test_common.h" #include "model.h" #include "texture.h" #include "local_array.h" class ModelTest : public QObject { Q_OBJECT private: private slots: void testSomething() { } }; QTEST_MAIN(ModelTest) #include "model_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_triangle_test.cc000066400000000000000000000776361466047437300223150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests triangle methods in the Model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "log.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" #include class ModelTriangleTest : public QObject { Q_OBJECT private: private slots: void initTestCase() { log_enable_debug( false ); } void testSelectTriangle() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unselected = newTestModel(); local_ptr rhs_selected = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_unselected.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 2, 1, 0 ); rhs_unselected->addVertex( 0, 0, 0 ); rhs_unselected->addVertex( 1, 0, 0 ); rhs_unselected->addVertex( 1, 1, 0 ); rhs_unselected->addTriangle( 0, 1, 2 ); rhs_unselected->addTriangle( 2, 1, 0 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->addTriangle( 2, 1, 0 ); rhs_selected->selectTriangle( 1 ); lhs->operationComplete( "Add triangles" ); lhs->selectTriangle( 1 ); QVERIFY_FALSE( lhs->isTriangleSelected( 0 ) ); QVERIFY_TRUE( lhs->isTriangleSelected( 1 ) ); lhs->operationComplete( "Select triangle" ); lhs->unselectTriangle( 1 ); QVERIFY_FALSE( lhs->isTriangleSelected( 0 ) ); QVERIFY_FALSE( lhs->isTriangleSelected( 1 ) ); lhs->operationComplete( "Unselect triangle" ); lhs->selectTriangle( 1 ); QVERIFY_FALSE( lhs->isTriangleSelected( 0 ) ); QVERIFY_TRUE( lhs->isTriangleSelected( 1 ) ); lhs->operationComplete( "Select triangle" ); lhs->unselectAll(); QVERIFY_FALSE( lhs->isTriangleSelected( 0 ) ); QVERIFY_FALSE( lhs->isTriangleSelected( 1 ) ); lhs->operationComplete( "Unselect all" ); checkUndoRedo( 5, lhs.get(), rhs_list ); } void testGetSelectedList() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 1, 2, 0 ); lhs->addTriangle( 2, 0, 1 ); lhs->addTriangle( 2, 1, 0 ); lhs->addTriangle( 1, 0, 2 ); lhs->addTriangle( 0, 2, 1 ); list tris; list::const_iterator it; lhs->getSelectedTriangles( tris ); QVERIFY(tris.begin() == tris.end()); QVERIFY_EQ( 0, (int) lhs->getSelectedTriangleCount() ); lhs->selectTriangle( 0 ); QVERIFY_EQ( 1, (int) lhs->getSelectedTriangleCount() ); lhs->selectTriangle( 5 ); QVERIFY_EQ( 2, (int) lhs->getSelectedTriangleCount() ); lhs->selectTriangle( 3 ); QVERIFY_EQ( 3, (int) lhs->getSelectedTriangleCount() ); lhs->selectTriangle( 1 ); QVERIFY_EQ( 4, (int) lhs->getSelectedTriangleCount() ); lhs->getSelectedTriangles( tris ); QVERIFY(tris.begin() != tris.end()); it = tris.begin(); QVERIFY(it != tris.end()); QVERIFY_EQ(0, *it ); ++it; QVERIFY(it != tris.end()); QVERIFY_EQ(1, *it ); ++it; QVERIFY(it != tris.end()); QVERIFY_EQ(3, *it ); ++it; QVERIFY(it != tris.end()); QVERIFY_EQ(5, *it ); ++it; QVERIFY(it == tris.end()); } // Tests that a selected triangle is deleted, but shared vertices // are not. void testDeleteSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unselected = newTestModel(); local_ptr rhs_selected = newTestModel(); local_ptr rhs_deleted = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_deleted.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 2, 0, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 3, 2, 1 ); rhs_unselected->addVertex( 0, 0, 0 ); rhs_unselected->addVertex( 1, 0, 0 ); rhs_unselected->addVertex( 1, 1, 0 ); rhs_unselected->addVertex( 2, 0, 0 ); rhs_unselected->addTriangle( 0, 1, 2 ); rhs_unselected->addTriangle( 3, 2, 1 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->addVertex( 2, 0, 0 ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->addTriangle( 3, 2, 1 ); rhs_selected->selectTriangle( 0 ); rhs_deleted->addVertex( 1, 0, 0 ); rhs_deleted->addVertex( 1, 1, 0 ); rhs_deleted->addVertex( 2, 0, 0 ); rhs_deleted->addTriangle( 2, 1, 0 ); lhs->operationComplete( "Add triangles" ); lhs->selectTriangle( 0 ); lhs->operationComplete( "Select triangle" ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } // Tests that vertices are deleted when no triangles are using them. void testDeleteSelectedNoFree() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unselected = newTestModel(); local_ptr rhs_selected = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_empty.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); rhs_unselected->addVertex( 0, 0, 0 ); rhs_unselected->addVertex( 1, 0, 0 ); rhs_unselected->addVertex( 1, 1, 0 ); rhs_unselected->addTriangle( 0, 1, 2 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->selectTriangle( 0 ); lhs->operationComplete( "Add triangles" ); lhs->selectTriangle( 0 ); lhs->operationComplete( "Select triangle" ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } // Tests that vertices are not deleted if they are marked free void testDeleteSelectedFree() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unselected = newTestModel(); local_ptr rhs_selected = newTestModel(); local_ptr rhs_deleted = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_deleted.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->setVertexFree( 1, true ); lhs->addTriangle( 0, 1, 2 ); rhs_unselected->addVertex( 0, 0, 0 ); rhs_unselected->addVertex( 1, 0, 0 ); rhs_unselected->addVertex( 1, 1, 0 ); rhs_unselected->setVertexFree( 1, true ); rhs_unselected->addTriangle( 0, 1, 2 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->setVertexFree( 1, true ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->selectTriangle( 0 ); rhs_deleted->addVertex( 1, 0, 0 ); rhs_deleted->setVertexFree( 0, true ); lhs->operationComplete( "Add triangles" ); lhs->selectTriangle( 0 ); lhs->operationComplete( "Select triangle" ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } void testHideTriangle() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unselected = newTestModel(); local_ptr rhs_selected = newTestModel(); local_ptr rhs_hidden = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_hidden.get() ); rhs_list.push_back( rhs_unselected.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 2, 0, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 1, 2, 3 ); rhs_unselected->addVertex( 0, 0, 0 ); rhs_unselected->addVertex( 1, 0, 0 ); rhs_unselected->addVertex( 1, 1, 0 ); rhs_unselected->addVertex( 2, 0, 0 ); rhs_unselected->addTriangle( 0, 1, 2 ); rhs_unselected->addTriangle( 1, 2, 3 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->addVertex( 2, 0, 0 ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->addTriangle( 1, 2, 3 ); rhs_selected->selectTriangle( 1 ); rhs_hidden->addVertex( 0, 0, 0 ); rhs_hidden->addVertex( 1, 0, 0 ); rhs_hidden->addVertex( 1, 1, 0 ); rhs_hidden->addVertex( 2, 0, 0 ); rhs_hidden->addTriangle( 0, 1, 2 ); rhs_hidden->addTriangle( 1, 2, 3 ); rhs_hidden->hideTriangle( 1 ); rhs_hidden->hideVertex( 3 ); // Have to do this directly lhs->operationComplete( "Add triangles" ); lhs->selectTriangle( 1 ); QVERIFY_TRUE( lhs->isTriangleVisible( 0 ) ); QVERIFY_TRUE( lhs->isTriangleVisible( 1 ) ); lhs->operationComplete( "Select triangle" ); lhs->hideSelected(); QVERIFY_TRUE( lhs->isTriangleVisible( 0 ) ); QVERIFY_FALSE( lhs->isTriangleVisible( 1 ) ); lhs->operationComplete( "Hide selected" ); lhs->unhideAll(); QVERIFY_TRUE( lhs->isTriangleVisible( 0 ) ); QVERIFY_TRUE( lhs->isTriangleVisible( 1 ) ); lhs->operationComplete( "Unhide all" ); checkUndoRedo( 4, lhs.get(), rhs_list ); } void testDeleteTriangle() { // deleteTriangle does not delete orphaned vertices (and these // aren't orphaned anyway) local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_full = newTestModel(); local_ptr rhs_deleted = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_full.get() ); rhs_list.push_back( rhs_deleted.get() ); QVERIFY_EQ( 0, (int) lhs->getTriangleCount() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); QVERIFY_EQ( 1, (int) lhs->getTriangleCount() ); lhs->addTriangle( 2, 1, 0 ); QVERIFY_EQ( 2, (int) lhs->getTriangleCount() ); rhs_full->addVertex( 0, 0, 0 ); rhs_full->addVertex( 1, 0, 0 ); rhs_full->addVertex( 1, 1, 0 ); rhs_full->addTriangle( 0, 1, 2 ); rhs_full->addTriangle( 2, 1, 0 ); rhs_deleted->addVertex( 0, 0, 0 ); rhs_deleted->addVertex( 1, 0, 0 ); rhs_deleted->addVertex( 1, 1, 0 ); rhs_deleted->addTriangle( 2, 1, 0 ); lhs->operationComplete( "Add triangles" ); lhs->deleteTriangle( 0 ); QVERIFY_EQ( 1, (int) lhs->getTriangleCount() ); lhs->operationComplete( "Delete triangle" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } // Tests that a triangle with an edge that uses the same vertex for // both end points is deleted by deleteFlattenedTriangles() void testDeleteFlattened() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_full = newTestModel(); local_ptr rhs_deleted = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_full.get() ); rhs_list.push_back( rhs_deleted.get() ); QVERIFY_EQ( 0, (int) lhs->getTriangleCount() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 1, 2, 2 ); lhs->addTriangle( 0, 0, 2 ); lhs->addTriangle( 1, 0, 1 ); lhs->addTriangle( 1, 1, 1 ); rhs_full->addVertex( 0, 0, 0 ); rhs_full->addVertex( 1, 0, 0 ); rhs_full->addVertex( 1, 1, 0 ); rhs_full->addTriangle( 0, 1, 2 ); rhs_full->addTriangle( 1, 2, 2 ); rhs_full->addTriangle( 0, 0, 2 ); rhs_full->addTriangle( 1, 0, 1 ); rhs_full->addTriangle( 1, 1, 1 ); rhs_deleted->addVertex( 0, 0, 0 ); rhs_deleted->addVertex( 1, 0, 0 ); rhs_deleted->addVertex( 1, 1, 0 ); rhs_deleted->addTriangle( 0, 1, 2 ); lhs->operationComplete( "Add triangles" ); lhs->deleteFlattenedTriangles(); lhs->operationComplete( "Delete flattened" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetVertices() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_front = newTestModel(); local_ptr rhs_back = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_front.get() ); rhs_list.push_back( rhs_back.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); rhs_front->addVertex( 0, 0, 0 ); rhs_front->addVertex( 1, 0, 0 ); rhs_front->addVertex( 1, 1, 0 ); rhs_front->addTriangle( 0, 1, 2 ); rhs_back->addVertex( 0, 0, 0 ); rhs_back->addVertex( 1, 0, 0 ); rhs_back->addVertex( 1, 1, 0 ); rhs_back->addTriangle( 2, 1, 0 ); lhs->operationComplete( "Add triangles" ); unsigned int vert[3]; QVERIFY_EQ( 0, lhs->getTriangleVertex( 0, 0 ) ); QVERIFY_EQ( 1, lhs->getTriangleVertex( 0, 1 ) ); QVERIFY_EQ( 2, lhs->getTriangleVertex( 0, 2 ) ); lhs->getTriangleVertices( 0, vert[0], vert[1], vert[2] ); QVERIFY_EQ( 0, (int) vert[0] ); QVERIFY_EQ( 1, (int) vert[1] ); QVERIFY_EQ( 2, (int) vert[2] ); lhs->setTriangleVertices( 0, 2, 1, 0 ); QVERIFY_EQ( 2, lhs->getTriangleVertex( 0, 0 ) ); QVERIFY_EQ( 1, lhs->getTriangleVertex( 0, 1 ) ); QVERIFY_EQ( 0, lhs->getTriangleVertex( 0, 2 ) ); lhs->getTriangleVertices( 0, vert[0], vert[1], vert[2] ); QVERIFY_EQ( 2, (int) vert[0] ); QVERIFY_EQ( 1, (int) vert[1] ); QVERIFY_EQ( 0, (int) vert[2] ); lhs->operationComplete( "Set vertices" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testTriangleTextureCoords() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unassigned = newTestModel(); local_ptr rhs_assigned = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unassigned.get() ); rhs_list.push_back( rhs_assigned.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 1, 2, 3 ); rhs_unassigned->addVertex( 0, 0, 0 ); rhs_unassigned->addVertex( 1, 0, 0 ); rhs_unassigned->addVertex( 1, 1, 0 ); rhs_unassigned->addVertex( 0, 1, 0 ); rhs_unassigned->addTriangle( 0, 1, 2 ); rhs_unassigned->addTriangle( 1, 2, 3 ); rhs_assigned->addVertex( 0, 0, 0 ); rhs_assigned->addVertex( 1, 0, 0 ); rhs_assigned->addVertex( 1, 1, 0 ); rhs_assigned->addVertex( 0, 1, 0 ); rhs_assigned->addTriangle( 0, 1, 2 ); rhs_assigned->addTriangle( 1, 2, 3 ); rhs_assigned->setTextureCoords( 0, 0, 0.0, 1.0 ); rhs_assigned->setTextureCoords( 0, 1, 0.1, 0.9 ); rhs_assigned->setTextureCoords( 0, 2, 0.2, 0.8 ); rhs_assigned->setTextureCoords( 1, 0, 0.5, 0.5 ); rhs_assigned->setTextureCoords( 1, 1, 0.6, 0.4 ); rhs_assigned->setTextureCoords( 1, 2, 0.7, 0.3 ); lhs->operationComplete( "Add triangle and projections" ); lhs->setTextureCoords( 0, 0, 0.0, 1.0 ); lhs->setTextureCoords( 0, 1, 0.1, 0.9 ); lhs->setTextureCoords( 0, 2, 0.2, 0.8 ); lhs->setTextureCoords( 1, 0, 0.5, 0.5 ); lhs->setTextureCoords( 1, 1, 0.6, 0.4 ); lhs->setTextureCoords( 1, 2, 0.7, 0.3 ); float s = 0.0; float t = 0.0; lhs->getTextureCoords( 0, 0, s, t ); QVERIFY_EQ( 0.0f, s ); QVERIFY_EQ( 1.0f, t ); lhs->getTextureCoords( 0, 1, s, t ); QVERIFY_EQ( 0.1f, s ); QVERIFY_EQ( 0.9f, t ); lhs->getTextureCoords( 0, 2, s, t ); QVERIFY_EQ( 0.2f, s ); QVERIFY_EQ( 0.8f, t ); lhs->getTextureCoords( 1, 0, s, t ); QVERIFY_EQ( 0.5f, s ); QVERIFY_EQ( 0.5f, t ); lhs->getTextureCoords( 1, 1, s, t ); QVERIFY_EQ( 0.6f, s ); QVERIFY_EQ( 0.4f, t ); lhs->getTextureCoords( 1, 2, s, t ); QVERIFY_EQ( 0.7f, s ); QVERIFY_EQ( 0.3f, t ); lhs->operationComplete( "Set texture coords" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testTriangleProjection() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_none = newTestModel(); local_ptr rhs_first = newTestModel(); local_ptr rhs_second = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_none.get() ); rhs_list.push_back( rhs_first.get() ); rhs_list.push_back( rhs_second.get() ); rhs_list.push_back( rhs_none.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->addProjection( "first", 0, 0, 0, Model::TPT_Plane ); lhs->addProjection( "second", 1, 1, 1, Model::TPT_Plane ); rhs_none->addVertex( 0, 0, 0 ); rhs_none->addVertex( 1, 0, 0 ); rhs_none->addVertex( 1, 1, 0 ); rhs_none->addTriangle( 0, 1, 2 ); rhs_none->addProjection( "first", 0, 0, 0, Model::TPT_Plane ); rhs_none->addProjection( "second", 1, 1, 1, Model::TPT_Plane ); rhs_first->addVertex( 0, 0, 0 ); rhs_first->addVertex( 1, 0, 0 ); rhs_first->addVertex( 1, 1, 0 ); rhs_first->addTriangle( 0, 1, 2 ); rhs_first->addProjection( "first", 0, 0, 0, Model::TPT_Plane ); rhs_first->addProjection( "second", 1, 1, 1, Model::TPT_Plane ); rhs_first->setTriangleProjection( 0, 0 ); rhs_second->addVertex( 0, 0, 0 ); rhs_second->addVertex( 1, 0, 0 ); rhs_second->addVertex( 1, 1, 0 ); rhs_second->addTriangle( 0, 1, 2 ); rhs_second->addProjection( "first", 0, 0, 0, Model::TPT_Plane ); rhs_second->addProjection( "second", 1, 1, 1, Model::TPT_Plane ); rhs_second->setTriangleProjection( 0, 1 ); QVERIFY_EQ( -1, lhs->getTriangleProjection( 0 ) ); lhs->operationComplete( "Add triangle and projections" ); lhs->setTriangleProjection( 0, 0 ); QVERIFY_EQ( 0, lhs->getTriangleProjection( 0 ) ); lhs->operationComplete( "Set first projection" ); lhs->setTriangleProjection( 0, 1 ); QVERIFY_EQ( 1, lhs->getTriangleProjection( 0 ) ); lhs->operationComplete( "Set second projection" ); lhs->setTriangleProjection( 0, -1 ); QVERIFY_EQ( -1, lhs->getTriangleProjection( 0 ) ); lhs->operationComplete( "Set no projection" ); checkUndoRedo( 4, lhs.get(), rhs_list ); } void testFlatNormal() { // +X { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 0, 1, 0 ); lhs->addVertex( 0, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->calculateNormals(); float expected[3] = { 1.0f, 0.0f, 0.0f }; float norm[3]; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 1, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 2, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); } // -X { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 0, 1, 1 ); lhs->addVertex( 0, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->calculateNormals(); float expected[3] = { -1.0f, 0.0f, 0.0f }; float norm[3]; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 1, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 2, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); } // +Y { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->calculateNormals(); float expected[3] = { 0.0f, 1.0f, 0.0f }; float norm[3]; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 1, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 2, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); } // -Y { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 0, 0, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->calculateNormals(); float expected[3] = { 0.0f, -1.0f, 0.0f }; float norm[3]; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 1, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 2, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); } // +Z { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->calculateNormals(); float expected[3] = { 0.0f, 0.0f, 1.0f }; float norm[3]; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 1, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 2, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); } // -Z { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addTriangle( 0, 1, 2 ); lhs->calculateNormals(); float expected[3] = { 0.0f, 0.0f, -1.0f }; float norm[3]; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 1, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); memset( norm, 0, sizeof(norm) ); lhs->getNormal( 0, 2, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); } } void testInvertNormals() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_front = newTestModel(); local_ptr rhs_back = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_front.get() ); rhs_list.push_back( rhs_back.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 0, 1, 0 ); lhs->addVertex( 0, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); rhs_front->addVertex( 0, 0, 0 ); rhs_front->addVertex( 0, 1, 0 ); rhs_front->addVertex( 0, 1, 1 ); rhs_front->addTriangle( 0, 1, 2 ); rhs_back->addVertex( 0, 0, 0 ); rhs_back->addVertex( 0, 1, 0 ); rhs_back->addVertex( 0, 1, 1 ); rhs_back->addTriangle( 2, 1, 0 ); lhs->operationComplete( "Add triangle" ); float norm[3]; float expected[3] = { 1.0f, 0.0f, 0.0f }; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( expected, 3, norm, 3 ); lhs->invertNormals( 0 ); lhs->operationComplete( "Invert normals" ); float inverted[3] = { -1.0f, 0.0f, 0.0f }; lhs->getFlatNormal( 0, norm ); QVERIFY_ARRAY_EQ( inverted, 3, norm, 3 ); // FIXME want to test that 0 and 2 swap texture coords? float s = 0; float t = 0; lhs->getTextureCoords( 0, 0, s, t ); rhs_back->setTextureCoords( 0, 0, s, t ); lhs->getTextureCoords( 0, 1, s, t ); rhs_back->setTextureCoords( 0, 1, s, t ); lhs->getTextureCoords( 0, 2, s, t ); rhs_back->setTextureCoords( 0, 2, s, t ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testFaceOutNone() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); // Nothing in front, must face out QVERIFY_FALSE( lhs->triangleFacesIn( 0 ) ); } void testFaceInBack() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 1, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 3, 4, 5 ); QVERIFY_TRUE( lhs->triangleFacesIn( 0 ) ); QVERIFY_FALSE( lhs->triangleFacesIn( 1 ) ); } void testFaceInFront() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 1, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 5, 4, 3 ); QVERIFY_TRUE( lhs->triangleFacesIn( 0 ) ); QVERIFY_TRUE( lhs->triangleFacesIn( 1 ) ); } void testFaceOutTwo() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 0, 0, 2 ); lhs->addVertex( 1, 0, 2 ); lhs->addVertex( 1, 1, 2 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 3, 4, 5 ); lhs->addTriangle( 6, 7, 8 ); // Hits an even number of triangles, must face out QVERIFY_FALSE( lhs->triangleFacesIn( 0 ) ); } void testFaceOutBack() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 1, 1, 1 ); lhs->addTriangle( 2, 1, 0 ); lhs->addTriangle( 3, 4, 5 ); QVERIFY_FALSE( lhs->triangleFacesIn( 0 ) ); QVERIFY_FALSE( lhs->triangleFacesIn( 1 ) ); } void testFaceInOneEdge() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 0, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 5, 4, 3 ); QVERIFY_FALSE( lhs->triangleFacesIn( 0 ) ); } void testFaceInTwoEdges() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 0, 1, 1 ); lhs->addVertex( 1, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 5, 4, 3 ); lhs->addTriangle( 5, 6, 4 ); // Hits two edges, must be inside QVERIFY_TRUE( lhs->triangleFacesIn( 0 ) ); } void testFaceOutFrontAndBack() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 0, 1 ); lhs->addVertex( 1, 0, 1 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 0, 0, -1 ); lhs->addVertex( 1, 0, -1 ); lhs->addVertex( 1, 1, -1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 3, 4, 5 ); lhs->addTriangle( 6, 7, 8 ); // Hits one in front and one in back. Something screwy is going on. // Can't be certain it is facing inward. QVERIFY_FALSE( lhs->triangleFacesIn( 0 ) ); } // FIXME Tests to add // // Triangle tests: // cosToPoint // subdivide // For each triangle // if contains one original point // contains no other original points // vector to other points is unchanged // else // one triangle contains three unique non-original points // normal matches original triangle // Triangle count and welded vertex count are correct // // FIXME test in other files: // simplify mesh? // select vertices from triangles // Group normal blending }; QTEST_MAIN(ModelTriangleTest) #include "model_triangle_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_vertex_test.cc000066400000000000000000000445411466047437300220120ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests vertex methods in the Model class. #include #include "test_common.h" #include "model.h" #include "texture.h" #include "log.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" #include class ModelVertexTest : public QObject { Q_OBJECT private: private slots: void initTestCase() { log_enable_debug( false ); } void testSelectVertex() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_unselected = newTestModel(); local_ptr rhs_selected = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_unselected.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_unselected.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); rhs_unselected->addVertex( 0, 0, 0 ); rhs_unselected->addVertex( 1, 0, 0 ); rhs_unselected->addVertex( 1, 1, 0 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->selectVertex( 1 ); lhs->operationComplete( "Add vertices" ); lhs->selectVertex( 1 ); QVERIFY_FALSE( lhs->isVertexSelected( 0 ) ); QVERIFY_TRUE( lhs->isVertexSelected( 1 ) ); QVERIFY_FALSE( lhs->isVertexSelected( 2 ) ); lhs->operationComplete( "Select vertex" ); lhs->unselectVertex( 1 ); QVERIFY_FALSE( lhs->isVertexSelected( 0 ) ); QVERIFY_FALSE( lhs->isVertexSelected( 1 ) ); QVERIFY_FALSE( lhs->isVertexSelected( 2 ) ); lhs->operationComplete( "Unselect vertex" ); lhs->selectVertex( 1 ); QVERIFY_FALSE( lhs->isVertexSelected( 0 ) ); QVERIFY_TRUE( lhs->isVertexSelected( 1 ) ); QVERIFY_FALSE( lhs->isVertexSelected( 2 ) ); lhs->operationComplete( "Select vertex" ); lhs->unselectAll(); QVERIFY_FALSE( lhs->isVertexSelected( 0 ) ); QVERIFY_FALSE( lhs->isVertexSelected( 1 ) ); QVERIFY_FALSE( lhs->isVertexSelected( 2 ) ); lhs->operationComplete( "Unselect all" ); checkUndoRedo( 5, lhs.get(), rhs_list ); } void testGetSelectedList() { local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 1, 1, 1 ); lhs->addVertex( 0, 1, 1 ); lhs->addVertex( 0, 0, 1 ); std::list verts; std::list::const_iterator it; lhs->getSelectedVertices( verts ); QVERIFY(verts.begin() == verts.end()); QVERIFY_EQ( 0, (int) lhs->getSelectedVertexCount() ); lhs->selectVertex( 0 ); QVERIFY_EQ( 1, (int) lhs->getSelectedVertexCount() ); lhs->selectVertex( 5 ); QVERIFY_EQ( 2, (int) lhs->getSelectedVertexCount() ); lhs->selectVertex( 3 ); QVERIFY_EQ( 3, (int) lhs->getSelectedVertexCount() ); lhs->selectVertex( 2 ); QVERIFY_EQ( 4, (int) lhs->getSelectedVertexCount() ); lhs->getSelectedVertices( verts ); QVERIFY(verts.begin() != verts.end()); it = verts.begin(); QVERIFY(it != verts.end()); QVERIFY_EQ(0, *it ); ++it; QVERIFY(it != verts.end()); QVERIFY_EQ(2, *it ); ++it; QVERIFY(it != verts.end()); QVERIFY_EQ(3, *it ); ++it; QVERIFY(it != verts.end()); QVERIFY_EQ(5, *it ); ++it; QVERIFY(it == verts.end()); } void testMoveVertex() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_orig = newTestModel(); local_ptr rhs_moved = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_orig.get() ); rhs_list.push_back( rhs_moved.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); rhs_orig->addVertex( 0, 0, 0 ); rhs_orig->addVertex( 1, 0, 0 ); rhs_orig->addVertex( 1, 1, 0 ); rhs_moved->addVertex( 3, 4, 5 ); rhs_moved->addVertex( 2, 1, 0 ); rhs_moved->addVertex( -3, -4, -5 ); double expected[3] = { 0, 0, 0 }; double actual[3] = { 0, 0, 0 }; expected[0] = 0; expected[1] = 0; expected[2] = 0; lhs->getVertexCoords( 0, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 1; expected[1] = 0; expected[2] = 0; lhs->getVertexCoords( 1, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 1; expected[1] = 1; expected[2] = 0; lhs->getVertexCoords( 2, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); lhs->operationComplete( "Add vertices" ); lhs->moveVertex( 0, 3, 4, 5 ); lhs->moveVertex( 1, 2, 1, 0 ); lhs->moveVertex( 2, -3, -4, -5 ); expected[0] = 3; expected[1] = 4; expected[2] = 5; lhs->getVertexCoords( 0, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = 2; expected[1] = 1; expected[2] = 0; lhs->getVertexCoords( 1, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); expected[0] = -3; expected[1] = -4; expected[2] = -5; lhs->getVertexCoords( 2, actual ); QVERIFY_ARRAY_EQ( expected, 3, actual, 3 ); lhs->operationComplete( "Move vertices" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testSetFree() { // setVertexFree is not an undoable operation local_ptr lhs = newTestModel(); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); QVERIFY_FALSE( lhs->isVertexFree( 0 ) ); QVERIFY_FALSE( lhs->isVertexFree( 1 ) ); QVERIFY_FALSE( lhs->isVertexFree( 2 ) ); lhs->setVertexFree( 1, true ); QVERIFY_FALSE( lhs->isVertexFree( 0 ) ); QVERIFY_TRUE( lhs->isVertexFree( 1 ) ); QVERIFY_FALSE( lhs->isVertexFree( 2 ) ); lhs->setVertexFree( 1, false ); QVERIFY_FALSE( lhs->isVertexFree( 0 ) ); QVERIFY_FALSE( lhs->isVertexFree( 1 ) ); QVERIFY_FALSE( lhs->isVertexFree( 2 ) ); } void testDeleteSelectedNoFreeNoneSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_full = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_full.get() ); rhs_list.push_back( rhs_empty.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); rhs_full->addVertex( 0, 0, 0 ); rhs_full->addVertex( 1, 0, 0 ); rhs_full->addVertex( 1, 1, 0 ); lhs->operationComplete( "Add vertices" ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); // No free vertices, they should be deleted even though they weren't // selected. checkUndoRedo( 2, lhs.get(), rhs_list ); } void testDeleteSelectedNoFreeAllSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_full = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_full.get() ); rhs_list.push_back( rhs_empty.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); rhs_full->addVertex( 0, 0, 0 ); rhs_full->addVertex( 1, 0, 0 ); rhs_full->addVertex( 1, 1, 0 ); lhs->operationComplete( "Add vertices" ); lhs->selectVertex( 0 ); lhs->selectVertex( 1 ); lhs->selectVertex( 2 ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); // No free vertices, they should be deleted even though they weren't // selected. checkUndoRedo( 2, lhs.get(), rhs_list ); } void testDeleteSelectedFreeNoneSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_full = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_full.get() ); // No change on the second operation, so there is nothing to undo lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->setVertexFree( 0, true ); lhs->setVertexFree( 1, true ); lhs->setVertexFree( 2, true ); rhs_full->addVertex( 0, 0, 0 ); rhs_full->addVertex( 1, 0, 0 ); rhs_full->addVertex( 1, 1, 0 ); rhs_full->setVertexFree( 0, true ); rhs_full->setVertexFree( 1, true ); rhs_full->setVertexFree( 2, true ); lhs->operationComplete( "Add vertices" ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); // No free vertices, they should be deleted even though they weren't // selected. checkUndoRedo( 1, lhs.get(), rhs_list ); } void testDeleteSelectedFreeAllSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_full = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_full.get() ); rhs_list.push_back( rhs_empty.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->setVertexFree( 0, true ); lhs->setVertexFree( 1, true ); lhs->setVertexFree( 2, true ); rhs_full->addVertex( 0, 0, 0 ); rhs_full->addVertex( 1, 0, 0 ); rhs_full->addVertex( 1, 1, 0 ); rhs_full->setVertexFree( 0, true ); rhs_full->setVertexFree( 1, true ); rhs_full->setVertexFree( 2, true ); lhs->operationComplete( "Add vertices" ); lhs->selectVertex( 0 ); lhs->selectVertex( 1 ); lhs->selectVertex( 2 ); lhs->deleteSelected(); lhs->operationComplete( "Delete selected" ); // No free vertices, they should be deleted even though they weren't // selected. checkUndoRedo( 2, lhs.get(), rhs_list ); } void testDeleteVertexDeletesTriangle() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_triangle = newTestModel(); local_ptr rhs_selected = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_triangle.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_empty.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); rhs_triangle->addVertex( 0, 0, 0 ); rhs_triangle->addVertex( 1, 0, 0 ); rhs_triangle->addVertex( 1, 1, 0 ); rhs_triangle->addTriangle( 0, 1, 2 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->selectVertex( 1 ); lhs->operationComplete( "Add vertices" ); // Deleting one vertex will delete one triangle, but not the other. // Other vertices remain undeleted. lhs->selectVertex( 1 ); lhs->operationComplete( "Select Vertex" ); lhs->deleteSelected(); lhs->operationComplete( "Delete Selected" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } void testDeleteVertexDeletesSelf() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_triangle = newTestModel(); local_ptr rhs_selected = newTestModel(); local_ptr rhs_deleted = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_triangle.get() ); rhs_list.push_back( rhs_selected.get() ); rhs_list.push_back( rhs_deleted.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 0, 2, 3 ); rhs_triangle->addVertex( 0, 0, 0 ); rhs_triangle->addVertex( 1, 0, 0 ); rhs_triangle->addVertex( 1, 1, 0 ); rhs_triangle->addVertex( 0, 1, 1 ); rhs_triangle->addTriangle( 0, 1, 2 ); rhs_triangle->addTriangle( 0, 2, 3 ); rhs_selected->addVertex( 0, 0, 0 ); rhs_selected->addVertex( 1, 0, 0 ); rhs_selected->addVertex( 1, 1, 0 ); rhs_selected->addVertex( 0, 1, 1 ); rhs_selected->addTriangle( 0, 1, 2 ); rhs_selected->addTriangle( 0, 2, 3 ); rhs_selected->selectVertex( 1 ); rhs_deleted->addVertex( 0, 0, 0 ); rhs_deleted->addVertex( 1, 1, 0 ); rhs_deleted->addVertex( 0, 1, 1 ); rhs_deleted->addTriangle( 0, 1, 2 ); lhs->operationComplete( "Add vertices" ); // Deleting one vertex will delete one triangle, but not the other. // Other vertices remain undeleted. lhs->selectVertex( 1 ); lhs->operationComplete( "Select Vertex" ); lhs->deleteSelected(); lhs->operationComplete( "Delete Selected" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } void testHideVertexHidesTriangle() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_visible = newTestModel(); local_ptr rhs_hidden = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_visible.get() ); rhs_list.push_back( rhs_hidden.get() ); rhs_list.push_back( rhs_visible.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addTriangle( 0, 1, 2 ); rhs_visible->addVertex( 0, 0, 0 ); rhs_visible->addVertex( 1, 0, 0 ); rhs_visible->addVertex( 1, 1, 0 ); rhs_visible->addTriangle( 0, 1, 2 ); rhs_hidden->addVertex( 0, 0, 0 ); rhs_hidden->addVertex( 1, 0, 0 ); rhs_hidden->addVertex( 1, 1, 0 ); rhs_hidden->addTriangle( 0, 1, 2 ); lhs->operationComplete( "Add vertices" ); // Hiding one vertex will hide the triangle, which hides the // other vertices too. lhs->selectVertex( 0 ); lhs->hideSelected(); rhs_hidden->hideVertex( 0 ); rhs_hidden->hideVertex( 1 ); rhs_hidden->hideVertex( 2 ); rhs_hidden->hideTriangle( 0 ); QVERIFY_FALSE( lhs->isVertexVisible(0) ); QVERIFY_FALSE( lhs->isVertexVisible(1) ); QVERIFY_FALSE( lhs->isVertexVisible(2) ); QVERIFY_FALSE( lhs->isTriangleVisible(0) ); lhs->operationComplete( "Hide Vertices" ); lhs->unhideAll(); QVERIFY_TRUE( lhs->isVertexVisible(0) ); QVERIFY_TRUE( lhs->isVertexVisible(1) ); QVERIFY_TRUE( lhs->isVertexVisible(2) ); QVERIFY_TRUE( lhs->isTriangleVisible(0) ); lhs->operationComplete( "Unhide All" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } void testHideVertexHidesSelf() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_visible = newTestModel(); local_ptr rhs_hidden = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_visible.get() ); rhs_list.push_back( rhs_hidden.get() ); rhs_list.push_back( rhs_visible.get() ); lhs->addVertex( 0, 0, 0 ); lhs->addVertex( 1, 0, 0 ); lhs->addVertex( 1, 1, 0 ); lhs->addVertex( 0, 1, 1 ); lhs->addTriangle( 0, 1, 2 ); lhs->addTriangle( 0, 2, 3 ); rhs_visible->addVertex( 0, 0, 0 ); rhs_visible->addVertex( 1, 0, 0 ); rhs_visible->addVertex( 1, 1, 0 ); rhs_visible->addVertex( 0, 1, 1 ); rhs_visible->addTriangle( 0, 1, 2 ); rhs_visible->addTriangle( 0, 2, 3 ); rhs_hidden->addVertex( 0, 0, 0 ); rhs_hidden->addVertex( 1, 0, 0 ); rhs_hidden->addVertex( 1, 1, 0 ); rhs_hidden->addVertex( 0, 1, 1 ); rhs_hidden->addTriangle( 0, 1, 2 ); rhs_hidden->addTriangle( 0, 2, 3 ); lhs->operationComplete( "Add vertices" ); // Hiding one vertex will hide one triangle, but not the other. // Other vertices remain visible. lhs->selectVertex( 1 ); lhs->hideSelected(); rhs_hidden->hideVertex( 1 ); rhs_hidden->hideTriangle( 0 ); QVERIFY_TRUE( lhs->isVertexVisible(0) ); QVERIFY_FALSE( lhs->isVertexVisible(1) ); QVERIFY_TRUE( lhs->isVertexVisible(2) ); QVERIFY_TRUE( lhs->isVertexVisible(3) ); QVERIFY_FALSE( lhs->isTriangleVisible(0) ); QVERIFY_TRUE( lhs->isTriangleVisible(1) ); lhs->operationComplete( "Hide Vertex" ); lhs->unhideAll(); QVERIFY_TRUE( lhs->isVertexVisible(0) ); QVERIFY_TRUE( lhs->isVertexVisible(1) ); QVERIFY_TRUE( lhs->isVertexVisible(2) ); QVERIFY_TRUE( lhs->isVertexVisible(3) ); QVERIFY_TRUE( lhs->isTriangleVisible(0) ); QVERIFY_TRUE( lhs->isTriangleVisible(1) ); lhs->operationComplete( "Unhide All" ); checkUndoRedo( 3, lhs.get(), rhs_list ); } // FIXME Tests to add (in other files): // // add/remove influences // Frame anim vertex tests // Vertex animated with (weighted) bone joints // Vertex indicies updated in triangles when vertices deleted }; QTEST_MAIN(ModelVertexTest) #include "model_vertex_test.moc" mm3d-1.3.15/src/tests/libmm3d/model_weld_test.cc000066400000000000000000000253241466047437300214260ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests vertex welding and unwelding #include #include "test_common.h" #include "model.h" #include "weld.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" class ModelWeldTest : public QObject { Q_OBJECT private: void addWeldTriangles( Model * m, int vertCount ) { if ( vertCount != 3 && vertCount != 4 && vertCount != 5 && vertCount != 6 ) { QTest::qFail( "Vert count was not 3-6", __FILE__, __LINE__ ); exit( -1 ); } m->addVertex( 0, 0, 0 ); m->addVertex( 1, 1, 0 ); m->addVertex( 1, 0, 0 ); if ( vertCount > 3 ) m->addVertex( 2, 0, 0 ); if ( vertCount == 5 ) { m->addVertex( 1, 0, 0 ); } else if ( vertCount == 6 ) { m->addVertex( 1, 1, 0 ); m->addVertex( 1, 0, 0 ); } if ( vertCount == 3 ) { m->addTriangle( 0, 1, 2 ); } else if ( vertCount == 4 ) { m->addTriangle( 0, 1, 2 ); m->addTriangle( 1, 3, 2 ); } else if ( vertCount == 5 ) { m->addTriangle( 0, 1, 2 ); m->addTriangle( 1, 3, 4 ); } else { m->addTriangle( 0, 1, 2 ); m->addTriangle( 4, 3, 5 ); } } void selectAllVertices( Model * m ) { size_t vcount = m->getVertexCount(); for ( size_t v = 0; v < vcount; ++v ) { m->selectVertex( v ); } } private slots: void initTestCase() { log_enable_debug( false ); log_enable_warning( true ); } void testWeldAllSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_fivevert = newTestModel(); local_ptr rhs_fourvert = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_fivevert.get() ); rhs_list.push_back( rhs_fourvert.get() ); addWeldTriangles( lhs.get(), 5 ); addWeldTriangles( rhs_fivevert.get(), 5 ); addWeldTriangles( rhs_fourvert.get(), 4 ); selectAllVertices( lhs.get() ); selectAllVertices( rhs_fivevert.get() ); selectAllVertices( rhs_fourvert.get() ); lhs->selectTriangle( 0 ); rhs_fivevert->selectTriangle( 0 ); rhs_fourvert->selectTriangle( 0 ); lhs->setTextureCoords( 0, 2, 0.3f, 0.4f ); lhs->setTextureCoords( 1, 2, 0.5f, 0.6f ); rhs_fivevert->setTextureCoords( 0, 2, 0.3f, 0.4f ); rhs_fivevert->setTextureCoords( 1, 2, 0.5f, 0.6f ); rhs_fourvert->setTextureCoords( 0, 2, 0.3f, 0.4f ); rhs_fourvert->setTextureCoords( 1, 2, 0.5f, 0.6f ); QVERIFY_TRUE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_FALSE( lhs->propEqual( rhs_fourvert.get() ) ); lhs->operationComplete( "Add triangles" ); int before = 0; int after = 0; weldSelectedVertices( lhs.get(), 0.0001, before, after ); QVERIFY_EQ( 2, before ); QVERIFY_EQ( 1, after ); QVERIFY_FALSE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_TRUE( lhs->propEqual( rhs_fourvert.get() ) ); lhs->operationComplete( "Weld selected" ); // Should be equivalent to both QVERIFY_TRUE( lhs->equivalent( rhs_fivevert.get() ) ); QVERIFY_TRUE( lhs->equivalent( rhs_fourvert.get() ) ); checkUndoRedo( 2, lhs.get(), rhs_list ); } // Nothing selected, nothing to weld void testWeldNoneSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_fivevert = newTestModel(); addWeldTriangles( lhs.get(), 5 ); addWeldTriangles( rhs_fivevert.get(), 5 ); QVERIFY_TRUE( lhs->propEqual( rhs_fivevert.get() ) ); weldSelectedVertices( lhs.get() ); QVERIFY_TRUE( lhs->propEqual( rhs_fivevert.get() ) ); } void testWeldTolerance() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_fivevert = newTestModel(); local_ptr rhs_fourvert = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_fivevert.get() ); rhs_list.push_back( rhs_fourvert.get() ); addWeldTriangles( lhs.get(), 5 ); addWeldTriangles( rhs_fivevert.get(), 5 ); addWeldTriangles( rhs_fourvert.get(), 4 ); selectAllVertices( lhs.get() ); selectAllVertices( rhs_fivevert.get() ); selectAllVertices( rhs_fourvert.get() ); lhs->moveVertex( 4, 1.1, 0, 0 ); rhs_fivevert->moveVertex( 4, 1.1, 0, 0 ); QVERIFY_TRUE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_FALSE( lhs->propEqual( rhs_fourvert.get() ) ); lhs->operationComplete( "Add triangles" ); // Not close enough to weld weldSelectedVertices( lhs.get(), 0.09 ); QVERIFY_TRUE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_FALSE( lhs->propEqual( rhs_fourvert.get() ) ); // Close enough to weld weldSelectedVertices( lhs.get(), 0.11 ); QVERIFY_FALSE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_TRUE( lhs->propEqual( rhs_fourvert.get() ) ); lhs->operationComplete( "Weld selected" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } // Tests that triangles that get two vertices merged into one // are deleted. void testWeldDeleteFlattened() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_fivevert = newTestModel(); local_ptr rhs_threevert = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_fivevert.get() ); rhs_list.push_back( rhs_threevert.get() ); addWeldTriangles( lhs.get(), 5 ); addWeldTriangles( rhs_fivevert.get(), 5 ); addWeldTriangles( rhs_threevert.get(), 3 ); selectAllVertices( lhs.get() ); selectAllVertices( rhs_fivevert.get() ); selectAllVertices( rhs_threevert.get() ); lhs->moveVertex( 3, 1, 0, 0 ); rhs_fivevert->moveVertex( 3, 1, 0, 0 ); QVERIFY_TRUE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_FALSE( lhs->propEqual( rhs_threevert.get() ) ); lhs->operationComplete( "Add triangles" ); int before = 0; int after = 0; weldSelectedVertices( lhs.get(), 0.0001, before, after ); QVERIFY_EQ( 3, before ); QVERIFY_EQ( 1, after ); QVERIFY_FALSE( lhs->propEqual( rhs_fivevert.get() ) ); QVERIFY_TRUE( lhs->propEqual( rhs_threevert.get() ) ); lhs->operationComplete( "Weld selected" ); checkUndoRedo( 2, lhs.get(), rhs_list ); } void testUnweldAllSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_empty = newTestModel(); local_ptr rhs_fourvert = newTestModel(); local_ptr rhs_sixvert = newTestModel(); ModelList rhs_list; rhs_list.push_back( rhs_empty.get() ); rhs_list.push_back( rhs_fourvert.get() ); rhs_list.push_back( rhs_sixvert.get() ); addWeldTriangles( lhs.get(), 4 ); addWeldTriangles( rhs_fourvert.get(), 4 ); addWeldTriangles( rhs_sixvert.get(), 6 ); selectAllVertices( lhs.get() ); selectAllVertices( rhs_sixvert.get() ); selectAllVertices( rhs_fourvert.get() ); lhs->selectTriangle( 0 ); rhs_sixvert->selectTriangle( 0 ); rhs_fourvert->selectTriangle( 0 ); lhs->setTextureCoords( 0, 2, 0.3f, 0.4f ); lhs->setTextureCoords( 1, 2, 0.5f, 0.6f ); rhs_sixvert->setTextureCoords( 0, 2, 0.3f, 0.4f ); rhs_sixvert->setTextureCoords( 1, 2, 0.5f, 0.6f ); rhs_fourvert->setTextureCoords( 0, 2, 0.3f, 0.4f ); rhs_fourvert->setTextureCoords( 1, 2, 0.5f, 0.6f ); lhs->addBoneJoint( "Parent", 1, 1, 1, 0, 0, 0 ); lhs->addBoneJoint( "Child", 1, 1, 1, 0, 0, 0, 0 ); rhs_sixvert->addBoneJoint( "Parent", 1, 1, 1, 0, 0, 0 ); rhs_sixvert->addBoneJoint( "Child", 1, 1, 1, 0, 0, 0, 0 ); rhs_fourvert->addBoneJoint( "Parent", 1, 1, 1, 0, 0, 0 ); rhs_fourvert->addBoneJoint( "Child", 1, 1, 1, 0, 0, 0, 0 ); lhs->addVertexInfluence( 2, 0, Model::IT_Custom, 0.7 ); lhs->addVertexInfluence( 2, 0, Model::IT_Custom, 0.3 ); rhs_sixvert->addVertexInfluence( 2, 0, Model::IT_Custom, 0.7 ); rhs_sixvert->addVertexInfluence( 2, 0, Model::IT_Custom, 0.3 ); rhs_sixvert->addVertexInfluence( 5, 0, Model::IT_Custom, 0.7 ); rhs_sixvert->addVertexInfluence( 5, 0, Model::IT_Custom, 0.3 ); rhs_fourvert->addVertexInfluence( 2, 0, Model::IT_Custom, 0.7 ); rhs_fourvert->addVertexInfluence( 2, 0, Model::IT_Custom, 0.3 ); QVERIFY_TRUE( lhs->propEqual( rhs_fourvert.get() ) ); QVERIFY_FALSE( lhs->propEqual( rhs_sixvert.get() ) ); lhs->operationComplete( "Add triangles" ); int before = 0; int after = 0; unweldSelectedVertices( lhs.get(), after, before ); QVERIFY_EQ( 4, before ); QVERIFY_EQ( 6, after ); QVERIFY_FALSE( lhs->propEqual( rhs_fourvert.get() ) ); QVERIFY_TRUE( lhs->propEqual( rhs_sixvert.get() ) ); lhs->operationComplete( "Unweld selected" ); // Should be equivalent to both QVERIFY_TRUE( lhs->equivalent( rhs_sixvert.get() ) ); QVERIFY_TRUE( lhs->equivalent( rhs_fourvert.get() ) ); checkUndoRedo( 2, lhs.get(), rhs_list ); } // Nothing selected, nothing to unweld void testUnweldNoneSelected() { local_ptr lhs = newTestModel(); local_ptr rhs_fourvert = newTestModel(); addWeldTriangles( lhs.get(), 4 ); addWeldTriangles( rhs_fourvert.get(), 4 ); QVERIFY_TRUE( lhs->propEqual( rhs_fourvert.get() ) ); unweldSelectedVertices( lhs.get() ); QVERIFY_TRUE( lhs->propEqual( rhs_fourvert.get() ) ); } }; QTEST_MAIN(ModelWeldTest) #include "model_weld_test.moc" mm3d-1.3.15/src/tests/libmm3d/msg_test.cc000066400000000000000000000114541466047437300201000ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests msg.cc #include #include #include #include #include #include "test_common.h" #include "msg.h" static int info_msg_count = 0; static int warning_msg_count = 0; static int error_msg_count = 0; static std::string display_msg = ""; void custom_info_msg( const char * msg ) { ++info_msg_count; display_msg = msg; } void custom_warning_msg( const char * msg ) { ++warning_msg_count; display_msg = msg; } void custom_error_msg( const char * msg ) { ++error_msg_count; display_msg = msg; } char prompt_yes( const char * str, const char * opts ) { display_msg = str; return 'Y'; } char prompt_no( const char * str, const char * opts ) { display_msg = str; return 'N'; } char prompt_cancel( const char * str, const char * opts ) { display_msg = str; return 'C'; } class MessageTest : public QObject { Q_OBJECT private slots: void init() { msg_register( NULL, NULL, NULL ); display_msg = ""; info_msg_count = 0; warning_msg_count = 0; error_msg_count = 0; } void testInfoMessage() { const std::string msg = "Info message"; msg_info( msg.c_str() ); QVERIFY_EQ( std::string(""), display_msg ); QVERIFY_EQ( 0, info_msg_count ); msg_register( custom_info_msg, custom_warning_msg, custom_error_msg ); msg_info( msg.c_str() ); QVERIFY_EQ( msg, display_msg ); QVERIFY_EQ( 1, info_msg_count ); QVERIFY_EQ( 0, warning_msg_count ); QVERIFY_EQ( 0, error_msg_count ); } void testWarningMessage() { const std::string msg = "Warning message"; msg_warning( msg.c_str() ); QVERIFY_EQ( std::string(""), display_msg ); QVERIFY_EQ( 0, info_msg_count ); msg_register( custom_info_msg, custom_warning_msg, custom_error_msg ); msg_warning( msg.c_str() ); QVERIFY_EQ( msg, display_msg ); QVERIFY_EQ( 0, info_msg_count ); QVERIFY_EQ( 1, warning_msg_count ); QVERIFY_EQ( 0, error_msg_count ); } void testErrorMessage() { const std::string msg = "Error message"; msg_error( msg.c_str() ); QVERIFY_EQ( std::string(""), display_msg ); QVERIFY_EQ( 0, info_msg_count ); msg_register( custom_info_msg, custom_warning_msg, custom_error_msg ); msg_error( msg.c_str() ); QVERIFY_EQ( msg, display_msg ); QVERIFY_EQ( 0, info_msg_count ); QVERIFY_EQ( 0, warning_msg_count ); QVERIFY_EQ( 1, error_msg_count ); } void testDefaultPrompt() { QVERIFY_EQ( 'Y', msg_info_prompt( "Yes message", "Ync" ) ); QVERIFY_EQ( 'Y', msg_info_prompt( "Yes message", "nYc" ) ); QVERIFY_EQ( 'Y', msg_info_prompt( "Yes message", "cnY" ) ); QVERIFY_EQ( 'N', msg_warning_prompt( "No message", "yNc" ) ); QVERIFY_EQ( 'N', msg_warning_prompt( "No message", "Nyc" ) ); QVERIFY_EQ( 'N', msg_warning_prompt( "No message", "ycN" ) ); QVERIFY_EQ( 'C', msg_error_prompt( "Cancel message", "ynC" ) ); QVERIFY_EQ( 'C', msg_error_prompt( "Cancel message", "yCn" ) ); QVERIFY_EQ( 'C', msg_error_prompt( "Cancel message", "Cyn" ) ); QVERIFY_EQ( 'Y', msg_error_prompt( "No default", "ync" ) ); QVERIFY_EQ( 'C', msg_error_prompt( "No default", "cny" ) ); QVERIFY_EQ( '\0', msg_error_prompt( "Empty message", "" ) ); QVERIFY_EQ( '\0', msg_error_prompt( "NULL message", NULL ) ); } void testCustomPrompt() { msg_register_prompt( prompt_yes, prompt_no, prompt_cancel ); std::string msg; msg = "Info prompt message"; QVERIFY_EQ( 'Y', msg_info_prompt( msg.c_str() ) ); QVERIFY_EQ( msg, display_msg ); msg = "Warning prompt message"; QVERIFY_EQ( 'N', msg_warning_prompt( msg.c_str() ) ); QVERIFY_EQ( msg, display_msg ); msg = "Error prompt message"; QVERIFY_EQ( 'C', msg_error_prompt( msg.c_str() ) ); QVERIFY_EQ( msg, display_msg ); } }; QTEST_MAIN(MessageTest) #include "msg_test.moc" mm3d-1.3.15/src/tests/libmm3d/new_mm3d_test.cc000066400000000000000000000057011466047437300210210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests the MM3D model file filter against a reference version. // FIXME add more models (particularly textured ones) #include #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "log.h" #include "mm3dfilter.h" #include "mm3dfilter_ref.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" Model * loadModelOrDie( const char * filename, bool useReference ) { Model * model = new Model; local_ptr f; if ( useReference ) f = new MisfitFilterRef; else f = new MisfitFilter; Model::ModelErrorE err = f->readFile( model, filename ); if ( err != Model::ERROR_NONE ) { fprintf( stderr, "fatal: %s: %s\n", filename, Model::errorToString( err ) ); delete model; exit( -1 ); } model->forceAddOrDelete( true ); return model; } //void model_status( Model * model, StatusTypeE type, unsigned ms, const char * fmt, ... ) //{ // // FIXME hack //} class NewMm3dTest : public QObject { Q_OBJECT private: void testModelFile( const char * file ) { // The lhs pointer is from the original filter local_ptr lhs = loadModelOrDie( file, true ); local_ptr rhs = loadModelOrDie( file, false ); QVERIFY_TRUE( lhs->propEqual( rhs.get() ) ); // FIXME should really use a temp file based on something // unique (hostname-pid?) so that multiple tests could run in parallel. const char tmpFile[] = "tmp_new_mm3d_test.mm3d"; MisfitFilter f; QVERIFY_EQ( Model::ERROR_NONE, f.writeFile( rhs.get(), tmpFile ) ); local_ptr written = loadModelOrDie( tmpFile, false ); QVERIFY_EQ( 0, unlink( tmpFile ) ); } private slots: void initTestCase() { log_enable_debug( false ); } void testModelEqualTest() { testModelFile( "data/model_equal_test.mm3d" ); } void testModelHiddenTest() { testModelFile( "data/model_hidden_test.mm3d" ); } }; QTEST_MAIN(NewMm3dTest) #include "new_mm3d_test.moc" mm3d-1.3.15/src/tests/libmm3d/texcompare_test.cc000066400000000000000000000076111466047437300214610ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests the equality of texture data #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "tgatex.h" #include "log.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" Texture * loadTextureOrDie( TextureFilter * f, const char * filename ) { Texture * tex = new Texture; Texture::ErrorE err = f->readFile( tex, filename ); if ( err != Texture::ERROR_NONE ) { fprintf( stderr, "fatal: %s: %s\n", filename, Texture::errorToString( err ) ); delete tex; exit( -1 ); } return tex; } Texture * loadTgaOrDie( const char * filename ) { TgaTextureFilter f; return loadTextureOrDie( &f, filename ); } Texture * loadOldTgaOrDie( const char * filename ) { OldTgaTextureFilter f; return loadTextureOrDie( &f, filename ); } class TextureCompareTest : public QObject { Q_OBJECT private slots: void initTestCase() { log_enable_debug( false ); } void testTgaOldNewRgbUncomp() { local_ptr lhs = loadOldTgaOrDie( "data/test_rgb_uncomp.tga" ); local_ptr rhs = loadTgaOrDie( "data/test_rgb_uncomp.tga" ); Texture::CompareResultT res; QVERIFY_TRUE( lhs->compare( rhs.get(), &res, 0 ) ); QVERIFY_TRUE( res.comparable ); QVERIFY_TRUE( res.pixelCount > 0 ); QVERIFY_EQ( res.pixelCount, res.matchCount ); QVERIFY_EQ( res.pixelCount, res.fuzzyCount ); } void testTgaOldNewRgbaUncomp() { local_ptr lhs = loadOldTgaOrDie( "data/test_rgba_uncomp.tga" ); local_ptr rhs = loadTgaOrDie( "data/test_rgba_uncomp.tga" ); Texture::CompareResultT res; QVERIFY_TRUE( lhs->compare( rhs.get(), &res, 0 ) ); QVERIFY_TRUE( res.comparable ); QVERIFY_TRUE( res.pixelCount > 0 ); QVERIFY_EQ( res.pixelCount, res.matchCount ); QVERIFY_EQ( res.pixelCount, res.fuzzyCount ); } void testTgaOldNewRgbComp() { local_ptr lhs = loadOldTgaOrDie( "data/test_rgb_comp.tga" ); local_ptr rhs = loadTgaOrDie( "data/test_rgb_comp.tga" ); Texture::CompareResultT res; QVERIFY_TRUE( lhs->compare( rhs.get(), &res, 0 ) ); QVERIFY_TRUE( res.comparable ); QVERIFY_TRUE( res.pixelCount > 0 ); QVERIFY_EQ( res.pixelCount, res.matchCount ); QVERIFY_EQ( res.pixelCount, res.fuzzyCount ); } void testTgaOldNewRgbaComp() { local_ptr lhs = loadOldTgaOrDie( "data/test_rgba_comp.tga" ); local_ptr rhs = loadTgaOrDie( "data/test_rgba_comp.tga" ); Texture::CompareResultT res; QVERIFY_TRUE( lhs->compare( rhs.get(), &res, 0 ) ); QVERIFY_TRUE( res.comparable ); QVERIFY_TRUE( res.pixelCount > 0 ); QVERIFY_EQ( res.pixelCount, res.matchCount ); QVERIFY_EQ( res.pixelCount, res.fuzzyCount ); } }; QTEST_MAIN(TextureCompareTest) #include "texcompare_test.moc" mm3d-1.3.15/src/tests/libmm3d/texscale_test.cc000066400000000000000000000071061466047437300211210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests the equality of texture data #include #include "test_common.h" #include "model.h" #include "texture.h" #include "modelstatus.h" #include "tgatex.h" #include "log.h" #include "texscale.h" #include "local_array.h" #include "local_ptr.h" #include "release_ptr.h" Texture * loadTextureOrDie( TextureFilter * f, const char * filename ) { Texture * tex = new Texture; Texture::ErrorE err = f->readFile( tex, filename ); if ( err != Texture::ERROR_NONE ) { fprintf( stderr, "fatal: %s: %s\n", filename, Texture::errorToString( err ) ); delete tex; exit( -1 ); } return tex; } Texture * loadTgaOrDie( const char * filename ) { TgaTextureFilter f; return loadTextureOrDie( &f, filename ); } class TextureScaleTest : public QObject { Q_OBJECT void scaleTexture( Texture * tex ) { uint8_t * oldData = tex->m_data; tex->m_data = texture_scale_auto( tex->m_data, tex->m_format, tex->m_width, tex->m_height ); delete[] oldData; } private slots: void initTestCase() { log_enable_debug( false ); } void testScaleDown() { local_ptr lhs = loadTgaOrDie( "data/test_rgb_uncomp.tga" ); local_ptr rhs = loadTgaOrDie( "data/test_rgb_300.tga" ); QVERIFY_FALSE( texture_scale_need_scale( lhs->m_width, lhs->m_height ) ); QVERIFY_TRUE( texture_scale_need_scale( rhs->m_width, rhs->m_height ) ); scaleTexture( rhs.get() ); Texture::CompareResultT res; QVERIFY_FALSE( lhs->compare( rhs.get(), &res, 0 ) ); QVERIFY_TRUE( res.comparable ); QVERIFY_EQ( 65536, (int) res.pixelCount ); QVERIFY_LT( (int) (65536.0 * 0.95), (int) res.matchCount ); QVERIFY_LT( (int) (65536.0 * 0.95), (int) res.fuzzyCount ); } void testScaleUp() { local_ptr lhs = loadTgaOrDie( "data/test_rgba_uncomp.tga" ); local_ptr rhs = loadTgaOrDie( "data/test_rgba_200.tga" ); QVERIFY_FALSE( texture_scale_need_scale( lhs->m_width, lhs->m_height ) ); QVERIFY_TRUE( texture_scale_need_scale( rhs->m_width, rhs->m_height ) ); scaleTexture( rhs.get() ); Texture::CompareResultT res; QVERIFY_FALSE( lhs->compare( rhs.get(), &res, 32 ) ); QVERIFY_TRUE( res.comparable ); QVERIFY_EQ( 65536, (int) res.pixelCount ); QVERIFY_LT( (int) (65536.0 * 0.91), (int) res.matchCount ); QVERIFY_LT( (int) (65536.0 * 0.95), (int) res.fuzzyCount ); } void testScaleZero() { QVERIFY_FALSE( texture_scale_need_scale( 0, 0 ) ); } }; QTEST_MAIN(TextureScaleTest) #include "texscale_test.moc" mm3d-1.3.15/src/tests/libmm3d/translation_test.cc000066400000000000000000000045001466047437300216420ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests translate.cc and mlocale.cc #include #include #include #include #include "test_common.h" #include "translate.h" #include "mlocale.h" std::string test_translate_function( const char * sourceStr ) { if ( strcmp( sourceStr, "foo" ) == 0 ) return "goo"; else if ( strcmp( sourceStr, "bar" ) == 0 ) return "ber"; return sourceStr; } class TranslationTest : public QObject { Q_OBJECT private slots: void testNoTranslate() { transll_install_handler( NULL ); QVERIFY_EQ( std::string("foo"), transll(QT_TRANSLATE_NOOP("Test", "foo")) ); QVERIFY_EQ( std::string("bar"), transll(QT_TRANSLATE_NOOP("Test", "bar")) ); QVERIFY_EQ( std::string("baz"), transll(QT_TRANSLATE_NOOP("Test", "baz")) ); } void testTranslate() { transll_install_handler( test_translate_function ); QVERIFY_EQ( std::string("goo"), transll(QT_TRANSLATE_NOOP("Test", "foo")) ); QVERIFY_EQ( std::string("ber"), transll(QT_TRANSLATE_NOOP("Test", "bar")) ); QVERIFY_EQ( std::string("baz"), transll(QT_TRANSLATE_NOOP("Test", "baz")) ); transll_install_handler( NULL ); } void testLocale() { QVERIFY_EQ( std::string(""), mlocale_get() ); mlocale_set("en-GB"); QVERIFY_EQ( std::string("en-GB"), mlocale_get() ); mlocale_set(""); QVERIFY_EQ( std::string(""), mlocale_get() ); } }; QTEST_MAIN(TranslationTest) #include "translation_test.moc" mm3d-1.3.15/src/tests/libmm3d/undomgr_test.cc000066400000000000000000000562511466047437300207710ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests local_ptr.h, local_array.h, release_ptr.h, // and file_closer.h #include #include #include #include #include "test_common.h" #include "log.h" #include "undomgr.h" #include "undo.h" class TestUndo : public Undo { public: TestUndo() : m_size( 0 ), m_useBaseSize( true ), m_undoReleaseCalled( false ), m_redoReleaseCalled( false ), m_releaseCalled( false ), m_canCombine( false ) { } virtual ~TestUndo() { } bool combine( Undo * u ) { TestUndo * undo = static_cast(u); return m_canCombine && undo->m_canCombine; } void undoRelease() { m_undoReleaseCalled = true; } void redoRelease() { m_redoReleaseCalled = true; } void release() { m_releaseCalled = true; } unsigned size() { return m_size + (m_useBaseSize ? Undo::size() : 0); } unsigned int m_size; bool m_useBaseSize; bool m_undoReleaseCalled; bool m_redoReleaseCalled; bool m_releaseCalled; bool m_canCombine; }; class UndoMgrTest : public QObject { Q_OBJECT private slots: void initTestCase() { log_enable_debug( false ); } void testBasicUndoTest() { TestUndo u1; TestUndo u2; TestUndo u3; UndoManager mgr; QVERIFY_FALSE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op1" ), std::string( mgr.getUndoOpName() ) ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op2" ), std::string( mgr.getUndoOpName() ) ); mgr.addUndo( &u3 ); mgr.operationComplete( "Op3" ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op3" ), std::string( mgr.getUndoOpName() ) ); QVERIFY_TRUE( NULL != mgr.undo() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_TRUE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op2" ), std::string( mgr.getUndoOpName() ) ); QVERIFY_EQ( std::string( "Op3" ), std::string( mgr.getRedoOpName() ) ); QVERIFY_TRUE( NULL != mgr.undo() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_TRUE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op1" ), std::string( mgr.getUndoOpName() ) ); QVERIFY_EQ( std::string( "Op2" ), std::string( mgr.getRedoOpName() ) ); QVERIFY_TRUE( NULL != mgr.undo() ); QVERIFY_FALSE( mgr.canUndo() ); QVERIFY_TRUE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op1" ), std::string( mgr.getRedoOpName() ) ); QVERIFY_TRUE( NULL == mgr.undo() ); QVERIFY_TRUE( NULL != mgr.redo() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_TRUE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op1" ), std::string( mgr.getUndoOpName() ) ); QVERIFY_EQ( std::string( "Op2" ), std::string( mgr.getRedoOpName() ) ); QVERIFY_TRUE( NULL != mgr.redo() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_TRUE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op2" ), std::string( mgr.getUndoOpName() ) ); QVERIFY_EQ( std::string( "Op3" ), std::string( mgr.getRedoOpName() ) ); QVERIFY_TRUE( NULL != mgr.redo() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); QVERIFY_EQ( std::string( "Op3" ), std::string( mgr.getUndoOpName() ) ); QVERIFY_TRUE( NULL == mgr.redo() ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); } void testUndoRelease() { TestUndo u1; TestUndo u2; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); mgr.undo(); mgr.redo(); mgr.undo(); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_TRUE( u2.m_redoReleaseCalled ); } void testUndoCombine() { TestUndo u1; TestUndo u2; TestUndo u3; TestUndo u4; u3.m_canCombine = true; u4.m_canCombine = true; UndoManager mgr; mgr.addUndo( &u1 ); mgr.addUndo( &u2 ); mgr.addUndo( &u3 ); mgr.addUndo( &u4 ); mgr.operationComplete( "Op1" ); UndoList * ul = mgr.undo(); QVERIFY_EQ( 3, (int) ul->size() ); QVERIFY_TRUE( NULL == mgr.undo() ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); } void testUndoNoCombine() { TestUndo u1; TestUndo u2; // Combine fails because they are separate operations u1.m_canCombine = true; u2.m_canCombine = true; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); UndoList * ul; ul = mgr.undo(); QVERIFY_EQ( 1, (int) ul->size() ); ul = mgr.undo(); QVERIFY_EQ( 1, (int) ul->size() ); QVERIFY_TRUE( NULL == mgr.undo() ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); } void testOperationCompleteWithoutOp() { TestUndo u1; TestUndo u2; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.operationComplete( "No Op" ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); UndoList * ul; QVERIFY_EQ( std::string( "Op2" ), std::string( mgr.getUndoOpName() ) ); ul = mgr.undo(); QVERIFY_EQ( 1, (int) ul->size() ); QVERIFY_EQ( std::string( "Op1" ), std::string( mgr.getUndoOpName() ) ); ul = mgr.undo(); QVERIFY_EQ( 1, (int) ul->size() ); QVERIFY_FALSE( mgr.canUndo() ); QVERIFY_TRUE( NULL == mgr.undo() ); } void testUndoOperationComplete() { TestUndo u1; TestUndo u2; TestUndo u3; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); QVERIFY_TRUE( NULL != mgr.undo() ); mgr.addUndo( &u3 ); mgr.operationComplete( "Op3" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_TRUE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_TRUE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_TRUE( u3.m_undoReleaseCalled ); } void testSizeLimit() { TestUndo u1; TestUndo u2; TestUndo u3; TestUndo u4; TestUndo u5; u1.m_size = 10; u1.m_useBaseSize = false; u2.m_size = 10; u2.m_useBaseSize = false; u3.m_size = 10; u3.m_useBaseSize = false; u4.m_size = 10; u4.m_useBaseSize = false; u5.m_size = 10; u5.m_useBaseSize = false; UndoManager mgr; mgr.setSizeLimit( 35 ); mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u3 ); mgr.operationComplete( "Op3" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u4 ); mgr.operationComplete( "Op4" ); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u5 ); mgr.operationComplete( "Op5" ); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_TRUE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); QVERIFY_TRUE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_TRUE( u2.m_undoReleaseCalled ); QVERIFY_TRUE( u3.m_undoReleaseCalled ); QVERIFY_TRUE( u4.m_undoReleaseCalled ); QVERIFY_TRUE( u5.m_undoReleaseCalled ); } void testCountLimit() { TestUndo u1; TestUndo u2; TestUndo u3; TestUndo u4; TestUndo u5; UndoManager mgr; mgr.setCountLimit( 3 ); mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u3 ); mgr.operationComplete( "Op3" ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_FALSE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u4 ); mgr.operationComplete( "Op4" ); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.addUndo( &u5 ); mgr.operationComplete( "Op5" ); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); QVERIFY_FALSE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_TRUE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u3.m_undoReleaseCalled ); QVERIFY_FALSE( u4.m_undoReleaseCalled ); QVERIFY_FALSE( u5.m_undoReleaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); QVERIFY_TRUE( u5.m_releaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); QVERIFY_FALSE( u3.m_redoReleaseCalled ); QVERIFY_FALSE( u4.m_redoReleaseCalled ); QVERIFY_FALSE( u5.m_redoReleaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_TRUE( u2.m_undoReleaseCalled ); QVERIFY_TRUE( u3.m_undoReleaseCalled ); QVERIFY_TRUE( u4.m_undoReleaseCalled ); QVERIFY_TRUE( u5.m_undoReleaseCalled ); } void testUndoCurrent() { { TestUndo u1; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); QVERIFY_TRUE( NULL == mgr.undoCurrent() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); } { TestUndo u1; TestUndo u2; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.addUndo( &u2 ); QVERIFY_TRUE( NULL != mgr.undoCurrent() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_TRUE( mgr.canRedo() ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_FALSE( u2.m_undoReleaseCalled ); QVERIFY_TRUE( u2.m_redoReleaseCalled ); } { TestUndo u1; TestUndo u2; UndoManager mgr; mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.addUndo( &u2 ); QVERIFY_TRUE( NULL != mgr.undoCurrent() ); QVERIFY_TRUE( NULL != mgr.redo() ); QVERIFY_TRUE( mgr.canUndo() ); QVERIFY_FALSE( mgr.canRedo() ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u1.m_undoReleaseCalled ); QVERIFY_FALSE( u1.m_redoReleaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u2.m_undoReleaseCalled ); QVERIFY_FALSE( u2.m_redoReleaseCalled ); } } void testListCombine() { // Without list combine { TestUndo u1; TestUndo u2; TestUndo u3; TestUndo u4; u2.m_canCombine = true; u4.m_canCombine = true; UndoManager mgr; mgr.addUndo( &u1 ); mgr.addUndo( &u2 ); mgr.addUndo( &u3 ); mgr.addUndo( &u4 ); mgr.operationComplete( "Op1" ); UndoList * ul = mgr.undo(); QVERIFY_EQ( 4, (int) ul->size() ); QVERIFY_TRUE( NULL == mgr.undo() ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_FALSE( u4.m_releaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); } // With list combine { TestUndo u1; TestUndo u2; TestUndo u3; TestUndo u4; u2.m_canCombine = true; u4.m_canCombine = true; UndoManager mgr; mgr.addUndo( &u1, true ); mgr.addUndo( &u2, true ); mgr.addUndo( &u3, true ); mgr.addUndo( &u4, true ); mgr.operationComplete( "Op1" ); UndoList * ul = mgr.undo(); QVERIFY_EQ( 3, (int) ul->size() ); QVERIFY_TRUE( NULL == mgr.undo() ); QVERIFY_FALSE( u1.m_releaseCalled ); QVERIFY_FALSE( u2.m_releaseCalled ); QVERIFY_FALSE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); mgr.clear(); QVERIFY_TRUE( u1.m_releaseCalled ); QVERIFY_TRUE( u2.m_releaseCalled ); QVERIFY_TRUE( u3.m_releaseCalled ); QVERIFY_TRUE( u4.m_releaseCalled ); } } void testSetSaved() { TestUndo u1; TestUndo u2; TestUndo u3; UndoManager mgr; mgr.setSaved(); mgr.addUndo( &u1 ); mgr.operationComplete( "Op1" ); mgr.addUndo( &u2 ); mgr.operationComplete( "Op2" ); mgr.addUndo( &u3 ); mgr.operationComplete( "Op3" ); // Save undo = 0 QVERIFY_FALSE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.undo(); QVERIFY_TRUE( mgr.isSaved() ); // Save undo = 1 mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.setSaved(); QVERIFY_TRUE( mgr.isSaved() ); mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); // Save undo = 2 mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.setSaved(); QVERIFY_TRUE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.redo(); QVERIFY_TRUE( mgr.isSaved() ); mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); // Save undo = 3 mgr.setSaved(); QVERIFY_TRUE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.undo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.redo(); QVERIFY_FALSE( mgr.isSaved() ); mgr.redo(); QVERIFY_TRUE( mgr.isSaved() ); } // Cover last of the undomgr.cc file void testOpNameTest() { TestUndo u1; UndoManager mgr; QVERIFY_EQ(std::string(""), std::string(mgr.getUndoOpName())); QVERIFY_EQ(std::string(""), std::string(mgr.getRedoOpName())); // Don't call operation complete here mgr.addUndo( &u1 ); QVERIFY_EQ(std::string(""), std::string(mgr.getUndoOpName())); } }; QTEST_MAIN(UndoMgrTest) #include "undomgr_test.moc" mm3d-1.3.15/src/tests/libmm3d/util_test.cc000066400000000000000000000035251466047437300202670ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2007-2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This file tests libmm3d/util.h #include #include "test_common.h" #include "util.h" class UtilTest : public QObject { Q_OBJECT private slots: void testClamp() { QVERIFY_EQ( 5, util_clamp(5, 2, 7 ) ); QVERIFY_EQ( 2, util_clamp(2, 2, 7 ) ); QVERIFY_EQ( 7, util_clamp(7, 2, 7 ) ); QVERIFY_EQ( 2, util_clamp(1, 2, 7 ) ); QVERIFY_EQ( 7, util_clamp(8, 2, 7 ) ); } void testWrapUp() { QVERIFY_EQ( 5, util_wrap_up(5, 2, 7 ) ); QVERIFY_EQ( 2, util_wrap_up(2, 2, 7 ) ); QVERIFY_EQ( 7, util_wrap_up(7, 2, 7 ) ); QVERIFY_EQ( 2, util_wrap_up(1, 2, 7 ) ); QVERIFY_EQ( 2, util_wrap_up(8, 2, 7 ) ); } void testWrapDown() { QVERIFY_EQ( 5, util_wrap_down(5, 2, 7 ) ); QVERIFY_EQ( 2, util_wrap_down(2, 2, 7 ) ); QVERIFY_EQ( 7, util_wrap_down(7, 2, 7 ) ); QVERIFY_EQ( 7, util_wrap_down(1, 2, 7 ) ); QVERIFY_EQ( 7, util_wrap_down(8, 2, 7 ) ); } }; QTEST_MAIN(UtilTest) #include "util_test.moc" mm3d-1.3.15/src/tests/profile/000077500000000000000000000000001466047437300160505ustar00rootroot00000000000000mm3d-1.3.15/src/tests/profile/Makefile000066400000000000000000000005221466047437300175070ustar00rootroot00000000000000include ../Makefile.common LIBS = ../../libmm3d/libmm3d.a # Add profile tests here PROFILES = \ load_md3.prof \ calc_fnormals.prof \ calc_normals.prof \ group_triangles.prof \ all: $(PROFILES) clean: rm -rf *.prof *.moc core core.* *.gcno *.gcda .cc.prof: $(CXX) $(CXXFLAGS) -pg -o $*.prof $< $(LIBS) $(TESTLIBS) mm3d-1.3.15/src/tests/profile/calc_fnormals.cc000066400000000000000000000024161466047437300211650ustar00rootroot00000000000000#include #include #include "model.h" #include "modelstatus.h" #include "mm3dfilter.h" #include "md3filter.h" #include "msg.h" char prompt_func( const char * str, const char * opts ) { return 'Y'; } void model_status( Model * model, StatusTypeE type, unsigned ms, const char * fmt, ... ) { // FIXME hack } Model * loadModel( const char * filename ) { Md3Filter f; Model * model = new Model; Model::ModelErrorE err = f.readFile( model, filename ); if ( err != Model::ERROR_NONE ) { fprintf( stderr, "%s: %s\n", filename, Model::errorToString( err ) ); delete model; return NULL; } return model; } int main( int argc, char * argv[] ) { if ( argc < 2 ) { fprintf( stderr, "No model files specified\n" ); return -1; } msg_register_prompt( prompt_func, prompt_func, prompt_func ); for ( int a = 1; a < argc; a++ ) { printf( "testing normal calculation for %s\n", argv[a] ); Model * model = loadModel( argv[a] ); if ( model ) { unsigned int acount = model->getAnimCount( Model::ANIMMODE_FRAME ); for ( unsigned int anim = 0; anim < acount; anim++ ) model->calculateFrameNormals( anim ); delete model; } } return 0; } mm3d-1.3.15/src/tests/profile/calc_normals.cc000066400000000000000000000017531466047437300210220ustar00rootroot00000000000000#include #include #include "model.h" #include "modelstatus.h" #include "mm3dfilter.h" void model_status( Model * model, StatusTypeE type, unsigned ms, const char * fmt, ... ) { // FIXME hack } Model * loadModel( const char * filename ) { MisfitFilter f; Model * model = new Model; Model::ModelErrorE err = f.readFile( model, filename ); if ( err != Model::ERROR_NONE ) { fprintf( stderr, "%s: %s\n", filename, Model::errorToString( err ) ); delete model; return NULL; } return model; } int main( int argc, char * argv[] ) { if ( argc < 2 ) { fprintf( stderr, "No model files specified\n" ); return -1; } for ( int a = 1; a < argc; a++ ) { printf( "testing normal calculation for %s\n", argv[a] ); Model * model = loadModel( argv[a] ); if ( model ) { for ( int t = 0; t < 25; ++t ) model->calculateNormals(); delete model; } } return 0; } mm3d-1.3.15/src/tests/profile/group_triangles.cc000066400000000000000000000046531466047437300215730ustar00rootroot00000000000000#include #include #include "model.h" #include "modelstatus.h" #include "mm3dfilter.h" void model_status( Model * model, StatusTypeE type, unsigned ms, const char * fmt, ... ) { // FIXME hack } Model * loadModel( const char * filename ) { MisfitFilter f; Model * model = new Model; Model::ModelErrorE err = f.readFile( model, filename ); if ( err != Model::ERROR_NONE ) { fprintf( stderr, "%s: %s\n", filename, Model::errorToString( err ) ); delete model; return NULL; } return model; } int main( int argc, char * argv[] ) { if ( argc < 2 ) { fprintf( stderr, "No model files specified\n" ); return -1; } for ( int a = 1; a < argc; a++ ) { printf( "testing group triangles for %s\n", argv[a] ); Model * model = loadModel( argv[a] ); if ( model ) { int tcount = model->getTriangleCount(); int grp = model->addGroup( "New group" ); // Up, Up for ( int t = 0; t < tcount; ++t ) { model->addTriangleToGroup( grp, t ); } for ( int t = 0; t < tcount; ++t ) { model->getTriangleGroup( t ); } for ( int t = 0; t < tcount; ++t ) { model->removeTriangleFromGroup( grp, t ); } // Up, Down for ( int t = 0; t < tcount; ++t ) { model->addTriangleToGroup( grp, t ); } for ( int t = 0; t < tcount; ++t ) { model->getTriangleGroup( t ); } for ( int t = tcount - 1; t > 0; --t ) { model->removeTriangleFromGroup( grp, t ); } // Down, Down for ( int t = tcount - 1; t > 0; --t ) { model->addTriangleToGroup( grp, t ); } for ( int t = 0; t < tcount; ++t ) { model->getTriangleGroup( t ); } for ( int t = tcount - 1; t > 0; --t ) { model->removeTriangleFromGroup( grp, t ); } // Down, Up for ( int t = tcount - 1; t > 0; --t ) { model->addTriangleToGroup( grp, t ); } for ( int t = 0; t < tcount; ++t ) { model->getTriangleGroup( t ); } for ( int t = 0; t > tcount; ++t ) { model->removeTriangleFromGroup( grp, t ); } } } return 0; } mm3d-1.3.15/src/tests/profile/load_md3.cc000066400000000000000000000020671466047437300200460ustar00rootroot00000000000000#include #include #include "model.h" #include "modelstatus.h" #include "mm3dfilter.h" #include "md3filter.h" #include "msg.h" char prompt_func( const char * str, const char * opts ) { return 'Y'; } void model_status( Model * model, StatusTypeE type, unsigned ms, const char * fmt, ... ) { // FIXME hack } Model * loadModel( const char * filename ) { Md3Filter f; Model * model = new Model; Model::ModelErrorE err = f.readFile( model, filename ); if ( err != Model::ERROR_NONE ) { fprintf( stderr, "%s: %s\n", filename, Model::errorToString( err ) ); delete model; return NULL; } return model; } int main( int argc, char * argv[] ) { if ( argc < 2 ) { fprintf( stderr, "No model files specified\n" ); return -1; } msg_register_prompt( prompt_func, prompt_func, prompt_func ); for ( int a = 1; a < argc; a++ ) { printf( "testing load time for %s\n", argv[a] ); Model * model = loadModel( argv[a] ); if ( model ) delete model; } return 0; } mm3d-1.3.15/src/tools/000077500000000000000000000000001466047437300144065ustar00rootroot00000000000000mm3d-1.3.15/src/tools/Makefile.am000066400000000000000000000041571466047437300164510ustar00rootroot00000000000000noinst_LIBRARIES = libtools.a libtools_HFILES = \ atrfartool.h \ atrneartool.h \ bgmovetool.h \ bgscaletool.h \ cubetool.h \ cubetoolwidget.h \ cylindertool.h \ cylindertoolwidget.h \ dragvertextool.h \ ellipsetool.h \ ellipsetoolwidget.h \ extrudetool.h \ jointtool.h \ pointtool.h \ projtool.h \ movetool.h \ polytool.h \ polytoolwidget.h \ projtoolwidget.h \ rectangletool.h \ rotatetool.h \ rotatetoolwidget.h \ scaletool.h \ scaletoolwidget.h \ selectbonetool.h \ selectpointtool.h \ selectprojtool.h \ selectconnectedtool.h \ selectfacetool.h \ selectfacetoolwidget.h \ selectgrouptool.h \ selectvertextool.h \ sheartool.h \ toolwidget.h \ torustool.h \ torustoolwidget.h \ vertextool.h libtools_MOC = \ cubetoolwidget.moc.cc \ cylindertoolwidget.moc.cc \ ellipsetoolwidget.moc.cc \ polytoolwidget.moc.cc \ projtoolwidget.moc.cc \ rotatetoolwidget.moc.cc \ scaletoolwidget.moc.cc \ toolwidget.moc.cc \ torustoolwidget.moc.cc \ selectfacetoolwidget.moc.cc BUILT_SOURCES = $(libtools_MOC) libtools_a_SOURCES = \ vertextool.cc \ atrfartool.cc \ atrneartool.cc \ bgmovetool.cc \ bgscaletool.cc \ cubetool.cc \ cubetoolwidget.cc \ cylindertool.cc \ cylindertoolwidget.cc \ dragvertextool.cc \ ellipsetool.cc \ ellipsetoolwidget.cc \ extrudetool.cc \ polytool.cc \ polytoolwidget.cc \ jointtool.cc \ pointtool.cc \ projtool.cc \ projtoolwidget.cc \ movetool.cc \ rotatetool.cc \ rotatetoolwidget.cc \ scaletool.cc \ scaletoolwidget.cc \ torustoolwidget.cc \ selectvertextool.cc \ selectconnectedtool.cc \ selectfacetool.cc \ selectfacetoolwidget.cc \ selectgrouptool.cc \ selectbonetool.cc \ selectpointtool.cc \ selectprojtool.cc \ sheartool.cc \ rectangletool.cc \ toolwidget.cc \ torustool.cc \ $(libtools_MOC) \ $(libtools_HFILES) AM_CPPFLAGS = $(CORE_PROFILE) $(COVFLAGS) -Wall -I$(srcdir)/../libmm3d -I$(srcdir)/../mm3dcore -I$(srcdir)/../depui -I$(builddir)/../qtui -I$(srcdir)/../implui -I$(srcdir)/../ -DMM3D_EDIT $(all_includes) $(QT_CXXFLAGS) $(LUALIB_CCFLAGS) $(GL_CFLAGS) %.moc.cc: %.h $(QT_MOC) -o $@ $< CLEANFILES = $(libtools_MOC) *.gcno *.gcda mm3d-1.3.15/src/tools/atrfartool.cc000066400000000000000000000076261466047437300171050ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "atrfartool.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "pixmap/atrfartool.xpm" #include "glmath.h" #include #include #include AttractFarTool::AttractFarTool() { } AttractFarTool::~AttractFarTool() { } const char * AttractFarTool::getPath() { return TOOLS_ATTRACT_MENU; } const char * AttractFarTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Attract Far" ); } bool AttractFarTool::getKeyBinding( int arg, int & keyBinding ) { return false; } void AttractFarTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_positionCoords.clear(); Model * model = parent->getModel(); list positions; model->getSelectedPositions( positions ); m_positionCoords.clear(); makeToolCoordList( parent, m_positionCoords, positions ); ToolCoordList::iterator it; bool firstSet = false; double curX = 0; double curY = 0; parent->getParentXYValue( x, y, curX, curY, true ); for ( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { (*it).dist = distance( curX, curY, (*it).oldCoords[0], (*it).oldCoords[1] ); // update range if ( !firstSet ) { m_minDistance = (*it).dist; m_maxDistance = (*it).dist; firstSet = true; } else { if ( (*it).dist < m_minDistance ) { m_minDistance = (*it).dist; } if ( (*it).dist > m_maxDistance ) { m_maxDistance = (*it).dist; } } } m_startX = curX; m_startY = curY; model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Attracting far selected primitives" ).toUtf8().data() ); } void AttractFarTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); double curX = 0; double curY = 0; parent->getParentXYValue( x, y, curX, curY ); double lengthX = curX - m_startX; double lengthY = curY - m_startY; ToolCoordList::iterator it; for( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { double x = (*it).oldCoords[0]; double y = (*it).oldCoords[1]; double z = (*it).newCoords[2]; if ( (m_maxDistance - m_minDistance) >= 0.00001f ) { x += (lengthX * ( ((*it).dist - m_minDistance) / (m_maxDistance - m_minDistance) ) ); y += (lengthY * ( ((*it).dist - m_minDistance) / (m_maxDistance - m_minDistance) ) ); } else { x += lengthX; y += lengthY; } movePosition( parent, (*it).pos, x, y, z ); } parent->updateAllViews(); } void AttractFarTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Attract far complete" ).toUtf8().data() ); } const char ** AttractFarTool::getPixmap() { return (const char **) atrfartool_xpm; } double AttractFarTool::max( double a, double b ) { return ( a > b ) ? a : b; } mm3d-1.3.15/src/tools/atrfartool.h000066400000000000000000000034501466047437300167360ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ATRFARTOOL_H #define __ATRFARTOOL_H #include "tool.h" #include "model.h" #include using std::list; class AttractFarTool : public Tool { public: AttractFarTool(); virtual ~AttractFarTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); double min( double a, double b ); double max( double a, double b ); protected: double m_minDistance; double m_maxDistance; double m_startX; double m_startY; ToolCoordList m_positionCoords; }; #endif // __ATRFARTOOL_H mm3d-1.3.15/src/tools/atrneartool.cc000066400000000000000000000076661466047437300172660ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "atrneartool.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "pixmap/atrneartool.xpm" #include "glmath.h" #include #include #include AttractNearTool::AttractNearTool() { } AttractNearTool::~AttractNearTool() { } const char * AttractNearTool::getPath() { return TOOLS_ATTRACT_MENU; } const char * AttractNearTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Attract Near" ); } bool AttractNearTool::getKeyBinding( int arg, int & keyBinding ) { return false; } void AttractNearTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_positionCoords.clear(); Model * model = parent->getModel(); list positions; model->getSelectedPositions( positions ); m_positionCoords.clear(); makeToolCoordList( parent, m_positionCoords, positions ); ToolCoordList::iterator it; bool firstSet = false; double curX = 0; double curY = 0; parent->getParentXYValue( x, y, curX, curY, true ); for ( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { (*it).dist = distance( curX, curY, (*it).oldCoords[0], (*it).oldCoords[1] ); // update range if ( !firstSet ) { m_minDistance = (*it).dist; m_maxDistance = (*it).dist; firstSet = true; } else { if ( (*it).dist < m_minDistance ) { m_minDistance = (*it).dist; } if ( (*it).dist > m_maxDistance ) { m_maxDistance = (*it).dist; } } } m_startX = curX; m_startY = curY; model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Attracting near selected primitives" ).toUtf8().data() ); } void AttractNearTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); double curX = 0; double curY = 0; parent->getParentXYValue( x, y, curX, curY ); double lengthX = curX - m_startX; double lengthY = curY - m_startY; ToolCoordList::iterator it; for( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { double x = (*it).oldCoords[0]; double y = (*it).oldCoords[1]; double z = (*it).newCoords[2]; if ( (m_maxDistance - m_minDistance) >= 0.00001f ) { x += (lengthX * ( 1.0 - ((*it).dist - m_minDistance) / (m_maxDistance - m_minDistance) ) ); y += (lengthY * ( 1.0 - ((*it).dist - m_minDistance) / (m_maxDistance - m_minDistance) ) ); } else { x += lengthX; y += lengthY; } movePosition( parent, (*it).pos, x, y, z ); } parent->updateAllViews(); } void AttractNearTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Attract near complete" ).toUtf8().data() ); } const char ** AttractNearTool::getPixmap() { return (const char **) atrneartool_xpm; } double AttractNearTool::max( double a, double b ) { return ( a > b ) ? a : b; } mm3d-1.3.15/src/tools/atrneartool.h000066400000000000000000000034561466047437300171210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ATRNEARTOOL_H #define __ATRNEARTOOL_H #include "tool.h" #include "model.h" #include using std::list; class AttractNearTool : public Tool { public: AttractNearTool(); virtual ~AttractNearTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); double min( double a, double b ); double max( double a, double b ); protected: double m_minDistance; double m_maxDistance; double m_startX; double m_startY; ToolCoordList m_positionCoords; }; #endif // __ATRNEARTOOL_H mm3d-1.3.15/src/tools/bgmovetool.cc000066400000000000000000000066631466047437300171050ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "bgmovetool.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "glmath.h" #include "pixmap/bgmovetool.xpm" #include BgMoveTool::BgMoveTool() { } BgMoveTool::~BgMoveTool() { } const char * BgMoveTool::getPath() { return TOOLS_BACKGROUND_MENU; } const char * BgMoveTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Move Background Image" ); } bool BgMoveTool::getKeyBinding( int arg, int & keyBinding ) { return false; } void BgMoveTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); Model * model = parent->getModel(); int index = (int) parent->getViewDirection() - 1; if ( index >= 0 ) { m_lastX = 0; m_lastX = 0; m_lastX = 0; parent->getXValue( x, y, &m_lastX ); parent->getYValue( x, y, &m_lastY ); parent->getZValue( x, y, &m_lastZ ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Moving background image" ).toUtf8().data() ); } else { model_status( model, StatusError, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Cannot move background from 3D view" ).toUtf8().data() ); } } void BgMoveTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); Model * model = parent->getModel(); int index = (int) parent->getViewDirection() - 1; if ( index >= 0 ) { float cenX = 0.0f; float cenY = 0.0f; float cenZ = 0.0f; double newX = 0.0f; double newY = 0.0f; double newZ = 0.0f; parent->getXValue( x, y, &newX ); parent->getYValue( x, y, &newY ); parent->getZValue( x, y, &newZ ); model->getBackgroundCenter( index, cenX, cenY, cenZ ); cenX += (newX - m_lastX); cenY += (newY - m_lastY); cenZ += (newZ - m_lastZ); model->setBackgroundCenter( index, cenX, cenY, cenZ ); m_lastX = newX; m_lastY = newY; m_lastZ = newZ; } parent->updateAllViews(); } void BgMoveTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Background move complete" ).toUtf8().data() ); } const char ** BgMoveTool::getPixmap() { return (const char **) bgmovetool_xpm; } void BgMoveTool::activated( int arg, Model * model, QMainWindow * mainwin ) { model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Move background image" ).toUtf8().data() ); } void BgMoveTool::deactivated() { //m_widget->close(); } mm3d-1.3.15/src/tools/bgmovetool.h000066400000000000000000000033231466047437300167350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BGMOVETOOL_H #define __BGMOVETOOL_H #include "tool.h" class BgMoveTool : public Tool { public: BgMoveTool(); virtual ~BgMoveTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: double m_lastX; double m_lastY; double m_lastZ; //BgMoveToolWidget * m_widget; }; #endif // __BGMOVETOOL_H mm3d-1.3.15/src/tools/bgscaletool.cc000066400000000000000000000103141466047437300172120ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "bgscaletool.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "glmath.h" #include "pixmap/bgscaletool.xpm" #include BgScaleTool::BgScaleTool() { } BgScaleTool::~BgScaleTool() { } const char * BgScaleTool::getPath() { return TOOLS_BACKGROUND_MENU; } const char * BgScaleTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Scale Background Image" ); } bool BgScaleTool::getKeyBinding( int arg, int & keyBinding ) { return false; } void BgScaleTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); Model * model = parent->getModel(); int index = (int) parent->getViewDirection() - 1; if ( index >= 0 ) { float cenX = 0.0f; float cenY = 0.0f; float cenZ = 0.0f; m_x = 0.0f; m_y = 0.0f; m_z = 0.0f; parent->getXValue( x, y, &m_x ); parent->getYValue( x, y, &m_y ); parent->getZValue( x, y, &m_z ); float tempX = m_x; float tempY = m_y; float tempZ = m_z; model->getBackgroundCenter( index, cenX, cenY, cenZ ); m_startScale = model->getBackgroundScale( index ); m_startLength = distance( cenX, cenY, cenZ, tempX, tempY, tempZ ); if ( m_startLength < 0.0001f ) { m_startLength = 0.0001f; } log_debug( "center (%f,%f,%f) mouse (%f,%f,%f)\n", cenX, cenY, cenZ, tempX, tempY, tempZ ); log_debug( "starting background scale with length %f, scale %f\n", m_startLength, m_startScale ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Scaling background image" ).toUtf8().data() ); } else { model_status( model, StatusError, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Cannot scale background from 3D view" ).toUtf8().data() ); } } void BgScaleTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); Model * model = parent->getModel(); int index = (int) parent->getViewDirection() - 1; if ( index >= 0 ) { float cenX = 0.0f; float cenY = 0.0f; float cenZ = 0.0f; double newX = 0.0f; double newY = 0.0f; double newZ = 0.0f; parent->getXValue( x, y, &newX ); parent->getYValue( x, y, &newY ); parent->getZValue( x, y, &newZ ); float tempX = newX; float tempY = newY; float tempZ = newZ; model->getBackgroundCenter( index, cenX, cenY, cenZ ); float length = distance( cenX, cenY, cenZ, tempX, tempY, tempZ ); float scale = length * (m_startScale / m_startLength ); log_debug( "scale with length %f, to scale %f\n", length, scale ); model->setBackgroundScale( index, scale ); } parent->updateAllViews(); } void BgScaleTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Background scale complete" ).toUtf8().data() ); } const char ** BgScaleTool::getPixmap() { return (const char **) bgscaletool_xpm; } void BgScaleTool::activated( int arg, Model * model, QMainWindow * mainwin ) { model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Scale background image" ).toUtf8().data() ); } void BgScaleTool::deactivated() { //m_widget->close(); } mm3d-1.3.15/src/tools/bgscaletool.h000066400000000000000000000034061466047437300170600ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __BGSCALETOOL_H #define __BGSCALETOOL_H #include "tool.h" class BgScaleTool : public Tool { public: BgScaleTool(); virtual ~BgScaleTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: double m_x; double m_y; double m_z; double m_startLength; double m_startScale; //BgScaleToolWidget * m_widget; }; #endif // __BGSCALETOOL_H mm3d-1.3.15/src/tools/cubetool.cc000066400000000000000000000200001466047437300165210ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "cubetool.h" #include "pixmap/cubetool.xpm" #include "model.h" #include "weld.h" #include "modelstatus.h" #include "log.h" #include #include #include using std::vector; static void _cubify( bool isCube, double & coord, double & diff_d, double & diff_s1, double & diff_s2 ) { if ( isCube ) { if ( fabs(diff_s1) > fabs(diff_s2) ) { if ( (diff_s1 < 0 && diff_s2 > 0) || (diff_s1 > 0 && diff_s2 < 0 ) ) { diff_s2 = -diff_s1; } else { diff_s2 = diff_s1; } } else { if ( (diff_s1 < 0 && diff_s2 > 0) || (diff_s1 > 0 && diff_s2 < 0 ) ) { diff_s1 = -diff_s2; } else { diff_s1 = diff_s2; } } } if ( fabs(diff_s1) < fabs(diff_s2) ) { coord = fabs(diff_s1/2.0); diff_d = -fabs(diff_s1); } else { coord = fabs(diff_s1/2.0); diff_d = -fabs(diff_s1); } } CubeTool::CubeTool() : m_tracking( false ), m_parent( NULL ) { } CubeTool::~CubeTool() { } void CubeTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( !m_tracking ) { m_parent = parent; // Keep track of which parent we're serving m_tracking = true; m_invertedNormals = false; m_x1 = 0.0; m_y1 = 0.0; m_z1 = 0.0; parent->getParentXYValue( x, y, m_x1, m_y1, true ); Model * model = parent->getModel(); model->unselectAll(); double x1 = 0.0; double y1 = 0.0; double x2 = 0.0; double y2 = 0.0; double z = 0.0; int xindex = 0; int yindex = 1; int zindex = 2; for ( unsigned side = 0; side < 6; side++ ) { if ( side & 1 ) { x1 = 1.0; x2 = 0.0; y1 = 1.0; y2 = 0.0; z = 1.0; } else { x1 = 0.0; x2 = 1.0; y1 = 1.0; y2 = 0.0; z = 0.0; } switch ( side ) { case 0: case 1: xindex = 0; yindex = 1; zindex = 2; break; case 2: case 3: xindex = 2; yindex = 1; zindex = 0; break; case 4: case 5: xindex = 0; yindex = 2; zindex = 1; break; default: break; } double coord[3]; for ( unsigned y = 0; y <= m_segments; y++ ) { for ( unsigned x = 0; x <= m_segments; x++ ) { coord[xindex] = x1 + ((x2 - x1) * (double) x / (double) m_segments); coord[yindex] = y1 + ((y2 - y1) * (double) y / (double) m_segments); coord[zindex] = z; ToolCoordT tc = addPosition( parent, Model::PT_Vertex, NULL, coord[0], coord[1], coord[2] ); log_debug( "adding vertex %d at %f,%f,%f\n", tc.pos.index, tc.oldCoords[0], tc.oldCoords[1], tc.oldCoords[2] ); m_vertices.push_back( tc ); } if ( y > 0 ) { int row1 = m_vertices.size() - (m_segments + 1) * 2; int row2 = m_vertices.size() - (m_segments + 1); for ( unsigned x = 0; x < m_segments; x++ ) { log_debug( "%d,%d,%d,%d\n", row1 + x, row1 + x + 1, row2 + x, row2 + x + 1 ); int t1 = model->addTriangle( m_vertices[ row2 + x ].pos.index, m_vertices[ row1 + x + 1].pos.index, m_vertices[ row1 + x ].pos.index ); int t2 = model->addTriangle( m_vertices[ row2 + x ].pos.index, m_vertices[ row2 + x + 1].pos.index, m_vertices[ row1 + x + 1].pos.index ); m_triangles.push_back( t1 ); m_triangles.push_back( t2 ); if ( side >= 2 ) { model->invertNormals( t1 ); model->invertNormals( t2 ); } } } } } unsigned count = m_vertices.size(); for ( unsigned sv = 0; sv < count; sv++ ) { model->selectVertex( m_vertices[sv].pos.index ); } updateVertexCoords( parent, m_x1, m_y1, m_z1, m_x1, m_y1, m_z1 ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Cube created" ).toUtf8().data() ); } } void CubeTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( parent != m_parent ) { log_error( "Can't serve two parents at once\n" ); } if ( m_tracking ) { m_tracking = false; double x2 = 0.0; double y2 = 0.0; double z2 = 0.0; parent->getParentXYValue( x, y, x2, y2 ); updateVertexCoords( parent, m_x1, m_y1, m_z1, x2, y2, z2 ); weldSelectedVertices( parent->getModel() ); parent->updateAllViews(); m_vertices.clear(); m_triangles.clear(); } } void CubeTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( parent != m_parent ) { log_error( "Can't serve two parents at once\n" ); } if ( m_tracking ) { double x2 = 0.0; double y2 = 0.0; double z2 = 0.0; parent->getParentXYValue( x, y, x2, y2 ); updateVertexCoords( parent, m_x1, m_y1, m_z1, x2, y2, z2 ); parent->updateAllViews(); } } void CubeTool::updateVertexCoords( Parent * parent, double x1, double y1, double z1, double x2, double y2, double z2 ) { Model * model = parent->getModel(); bool invert = false; double xdiff = x2 - x1; double ydiff = y2 - y1; double zdiff = z2 - z1; _cubify( m_isCube, z1, zdiff, xdiff, ydiff ); if ( y1 < y2 ) { invert = !invert; } if ( x2 > x1 ) { invert = !invert; } ToolCoordList::iterator it; for ( it = m_vertices.begin(); it != m_vertices.end(); it++ ) { movePosition( parent, (*it).pos, (*it).oldCoords[0]*xdiff + x1, (*it).oldCoords[1]*ydiff + y1, (*it).oldCoords[2]*zdiff + z1 ); } if ( invert != m_invertedNormals ) { unsigned count = m_triangles.size(); for ( unsigned t = 0; t < count; t++ ) { model->invertNormals( m_triangles[t] ); } m_invertedNormals = invert; } } void CubeTool::activated( int arg, Model * model, QMainWindow * mainwin ) { m_widget = new CubeToolWidget( this, mainwin ); m_widget->show(); } void CubeTool::deactivated() { m_widget->close(); } const char ** CubeTool::getPixmap() { return (const char **) cubetool_xpm; } void CubeTool::setCubeValue( bool newValue ) { m_isCube = newValue; log_debug( "isCube = %s\n", newValue ? "true" : "false" ); } void CubeTool::setSegmentValue( int newValue ) { if ( newValue >= 1 ) { m_segments = newValue; } } const char * CubeTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Cube" ); } mm3d-1.3.15/src/tools/cubetool.h000066400000000000000000000042061466047437300163750ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CUBETOOL_H #define __CUBETOOL_H #include "tool.h" #include "cubetoolwidget.h" #include class CubeTool : public ::Tool, public CubeToolWidget::Observer { public: CubeTool(); virtual ~CubeTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool isCreation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // Observer methods void setCubeValue( bool newValue ); void setSegmentValue( int newValue ); protected: bool m_tracking; Parent * m_parent; CubeToolWidget * m_widget; bool m_isCube; unsigned m_segments; void updateVertexCoords( Parent *, double x1, double y1, double z1, double x2, double y2, double z2 ); ToolCoordList m_vertices; std::vector m_triangles; double m_x1; double m_y1; double m_z1; bool m_invertedNormals; }; #endif // __CUBETOOL_H mm3d-1.3.15/src/tools/cubetoolwidget.cc000066400000000000000000000060441466047437300177410ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "cubetoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include #include #include CubeToolWidget::CubeToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget( parent ), m_observer( observer ) { const int DEFAULT_SEGMENT = 1; const bool DEFAULT_CUBE = false; m_layout = boxLayout(); m_cubeLabel = new QLabel( tr("Cube"), mainWidget() ); m_layout->addWidget( m_cubeLabel ); m_cubeValue = new QCheckBox( mainWidget() ); m_layout->addWidget( m_cubeValue ); bool isCube = DEFAULT_CUBE; if ( g_prefs.exists( "ui_cubetool_iscube" ) ) { isCube = (g_prefs( "ui_cubetool_iscube" ).intValue() != 0) ? true : false; } m_cubeValue->setChecked( isCube ); m_segmentLabel = new QLabel( tr("Segment"), mainWidget() ); m_layout->addWidget( m_segmentLabel ); m_segmentValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_segmentValue ); m_segmentValue->setMinimum( 1 ); m_segmentValue->setMaximum( 25 ); int segmentVal = DEFAULT_SEGMENT; if ( g_prefs.exists( "ui_cubetool_segment" ) ) { int val = g_prefs( "ui_cubetool_segment" ).intValue(); if ( val >= 1 && val <= 25 ) { segmentVal = val; } } m_segmentValue->setValue( segmentVal ); m_layout->addStretch(); connect( m_cubeValue, SIGNAL(toggled(bool)), this, SLOT(cubeValueChanged(bool)) ); connect( m_segmentValue, SIGNAL(valueChanged(int)), this, SLOT(segmentValueChanged(int)) ); m_cubeLabel->show(); m_cubeValue->show(); m_segmentLabel->show(); m_segmentValue->show(); cubeValueChanged( isCube ); segmentValueChanged( segmentVal ); m_layout->addStretch(); } CubeToolWidget::~CubeToolWidget() { } void CubeToolWidget::segmentValueChanged( int newValue ) { g_prefs( "ui_cubetool_segment" ) = newValue; m_observer->setSegmentValue( newValue ); } void CubeToolWidget::cubeValueChanged( bool newValue ) { g_prefs( "ui_cubetool_iscube" ) = newValue ? 1 : 0; m_observer->setCubeValue( newValue ); } mm3d-1.3.15/src/tools/cubetoolwidget.h000066400000000000000000000034771466047437300176120ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CUBETOOLWIDGET_H #define __CUBETOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QSpinBox; class QCheckBox; class QLabel; #include "toolwidget.h" class CubeToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setCubeValue( bool newValue ) = 0; virtual void setSegmentValue( int newValue ) = 0; }; CubeToolWidget( Observer * observer, QMainWindow * parent ); virtual ~CubeToolWidget(); public slots: void cubeValueChanged( bool cube ); void segmentValueChanged( int newValue ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_cubeLabel; QCheckBox * m_cubeValue; QLabel * m_segmentLabel; QSpinBox * m_segmentValue; }; #endif // __CUBETOOLWIDGET_H mm3d-1.3.15/src/tools/cylindertool.cc000066400000000000000000000222441466047437300174300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "cylindertool.h" #include "pixmap/cylindertool.xpm" #include "model.h" #include "glmath.h" #include "log.h" #include "modelstatus.h" #include #include #include using std::vector; using std::list; CylinderTool::CylinderTool() : m_segments( 1 ), m_sides( 8 ), m_inverted( false ) { } CylinderTool::~CylinderTool() { } void CylinderTool::activated( int arg, Model * model, QMainWindow * mainwin ) { log_debug( "cylinder activated\n" ); m_widget = new CylinderToolWidget( this, mainwin ); m_widget->show(); } void CylinderTool::deactivated() { m_widget->close(); } void CylinderTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); model->unselectAll(); double pos[3] = {0,0,0}; double rad[3] = {0,0,0}; m_inverted = false; parent->getParentXYValue( x, y, pos[0], pos[1], true ); m_startX = pos[0]; m_startY = pos[1]; unsigned triStart = model->getTriangleCount(); unsigned points = m_sides; ToolCoordT ev; unsigned s; unsigned r; // Create external cylinder for ( s = 0; s <= m_segments; s++ ) { double scale = (double) m_scale / 100.0; scale = (double) m_scale / 100.0; scale = (1.0 - scale) * ((double) s / (double) m_segments) //* (sqrt( 10000.0 * (double) s / (double) m_segments ) / 100.0) //* ( sin( ((double) s / (double) m_segments) * PI / 2.0 ) ) + scale; for ( r = 0; r < points; r++ ) { double vx = (double) s / (double) (m_segments); double vy = cos( ((double) r / (double) points) * PI * 2.0 ) * scale; double vz = sin( ((double) r / (double) points) * PI * 2.0 ) * scale; ev = addPosition( parent, Model::PT_Vertex, NULL, vx, vy, vz ); m_vertices.push_back( ev ); } if ( s > 0 ) { for ( r = 0; r < points; r++ ) { unsigned next = r + 1; if ( next >= points ) { next = 0; } model->addTriangle( m_vertices[((s-1)*points) + r].pos.index, m_vertices[ ((s-1)*points) + next].pos.index, m_vertices[ (s*points) + r ].pos.index ); model->addTriangle( m_vertices[((s-1)*points) + next].pos.index, m_vertices[ (s*points) + next].pos.index, m_vertices[ (s*points) + r ].pos.index ); } } } unsigned off = m_vertices.size(); // Create internal cylinder if ( m_width < 100 ) { double rad = 1.0 - (double) m_width / 100.0; if ( rad > 0.999 ) { rad = 0.999; } for ( s = 0; s <= m_segments; s++ ) { double scale = (double) m_scale / 100.0; scale = (1.0 - scale) * ((double) s / (double) m_segments) + scale; for ( r = 0; r < points; r++ ) { double vx = (double) s / (double) (m_segments); double vy = cos( ((double) r / (double) points) * PI * 2.0 ) * rad * scale; double vz = sin( ((double) r / (double) points) * PI * 2.0 ) * rad * scale; ev = addPosition( parent, Model::PT_Vertex, NULL, vx, vy, vz ); m_vertices.push_back( ev ); } if ( s > 0 ) { for ( r = 0; r < points; r++ ) { unsigned next = r + 1; if ( next >= points ) { next = 0; } model->addTriangle( m_vertices[ off + ((s-1)*points) + next ].pos.index, m_vertices[ off + ((s-1)*points) + r ].pos.index, m_vertices[ off + (s*points) + r ].pos.index ); model->addTriangle( m_vertices[ off + ((s-1)*points) + next ].pos.index, m_vertices[ off + (s*points) + r ].pos.index, m_vertices[ off + (s*points) + next ].pos.index ); } } } // Add ends unsigned off2 = m_vertices.size() - points; for ( r = 0; r < points; r++ ) { unsigned next = r + 1; if ( next >= points ) { next = 0; } model->addTriangle( m_vertices[ next ].pos.index, m_vertices[ r ].pos.index, m_vertices[ off + r ].pos.index ); model->addTriangle( m_vertices[ next ].pos.index, m_vertices[ off + r ].pos.index, m_vertices[ off + next ].pos.index ); model->addTriangle( m_vertices[ (off - points) + r ].pos.index, m_vertices[ (off - points) + next ].pos.index, m_vertices[ off2 + r ].pos.index ); model->addTriangle( m_vertices[ (off - points) + next ].pos.index, m_vertices[ off2 + next ].pos.index, m_vertices[ off2 + r ].pos.index ); } } else // No internal cylinder { off = m_vertices.size() - points; // Add ends ev = addPosition( parent, Model::PT_Vertex, NULL, 0, 0, 0 ); for ( r = 0; r < points; r++ ) { unsigned next = r + 1; if ( next >= points ) { next = 0; } model->addTriangle( ev.pos.index, m_vertices[ next ].pos.index, m_vertices[ r ].pos.index ); } m_vertices.push_back( ev ); ev = addPosition( parent, Model::PT_Vertex, NULL, 1, 0, 0 ); m_vertices.push_back( ev ); for ( r = 0; r < points; r++ ) { unsigned next = r + 1; if ( next >= points ) { next = 0; } model->addTriangle( ev.pos.index, m_vertices[ off + r].pos.index, m_vertices[ off + next].pos.index ); } m_vertices.push_back( ev ); } unsigned triEnd = model->getTriangleCount(); for ( unsigned t = triStart; t < triEnd; t++ ) { model->selectTriangle( t ); } updateVertexCoords( parent, pos[0], pos[1], pos[2], rad[0], rad[1], rad[2] ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Cylinder created" ).toUtf8().data() ); } void CylinderTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { m_vertices.clear(); } void CylinderTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); double pos[3] = {0,0,0}; double rad[3] = {0,0,0}; parent->getParentXYValue( x, y, pos[0], pos[1] ); bool doInvert = false; rad[0] = (pos[0] - m_startX); rad[1] = fabs(m_startY - pos[1]) / 2; rad[2] = fabs(m_startY - pos[1]) / 2; pos[0] = m_startX; pos[1] = (pos[1] + m_startY) / 2; pos[2] = 0; if ( rad[0] < 0.0 && m_inverted == false ) { doInvert = true; } else if ( rad[0] >= 0.0 && m_inverted == true ) { doInvert = true; } if ( doInvert ) { m_inverted = !m_inverted; list selectedList; model->getSelectedTriangles( selectedList ); list::iterator it; for ( it = selectedList.begin(); it != selectedList.end(); it++ ) { model->invertNormals( *it ); } } updateVertexCoords( parent, pos[0], pos[1], pos[2], rad[0], rad[1], rad[2] ); parent->updateAllViews(); } const char ** CylinderTool::getPixmap() { return (const char **) cylindertool_xpm; } void CylinderTool::updateVertexCoords( Tool::Parent * parent, double x, double y, double z, double xrad, double yrad, double zrad ) { ToolCoordList::iterator it; for ( it = m_vertices.begin(); it != m_vertices.end(); it++ ) { movePosition( parent, (*it).pos, (*it).oldCoords[0]*xrad + x, (*it).oldCoords[1]*yrad + y, (*it).oldCoords[2]*zrad + z ); } } void CylinderTool::setSegmentsValue( int newValue ) { log_debug( "segments: %d\n", newValue ); m_segments = newValue; } void CylinderTool::setSidesValue( int newValue ) { log_debug( "sides: %d\n", newValue ); m_sides = newValue; } void CylinderTool::setWidthValue( int newValue ) { log_debug( "width: %d\n", newValue ); m_width = newValue; } void CylinderTool::setScaleValue( int newValue ) { log_debug( "scale: %d\n", newValue ); m_scale = newValue; } void CylinderTool::setShapeValue( int newValue ) { log_debug( "shape: %d\n", newValue ); m_shape = newValue; } const char * CylinderTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Cylinder" ); } mm3d-1.3.15/src/tools/cylindertool.h000066400000000000000000000043771466047437300173010ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CYLINDERTOOL_H #define __CYLINDERTOOL_H #include "tool.h" #include "cylindertoolwidget.h" #include class CylinderTool : public Tool, public CylinderToolWidget::Observer { public: CylinderTool(); virtual ~CylinderTool(); int getToolCount() { return 1; }; const char * getName( int arg ); void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // CylinderToolWidget::Observer void setSegmentsValue( int newValue ); void setSidesValue( int newValue ); void setWidthValue( int newValue ); void setScaleValue( int newValue ); void setShapeValue( int newValue ); protected: void updateVertexCoords( Tool::Parent *, double x, double y, double z, double xrad, double yrad, double zrad ); CylinderToolWidget * m_widget; unsigned m_segments; unsigned m_sides; unsigned m_width; unsigned m_scale; unsigned m_shape; bool m_inverted; ToolCoordList m_vertices; double m_startX; double m_startY; }; #endif // __CYLINDERTOOL_H mm3d-1.3.15/src/tools/cylindertoolwidget.cc000066400000000000000000000133301466047437300206300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "cylindertoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include CylinderToolWidget::CylinderToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const int DEFAULT_SEGMENTS = 4; const int DEFAULT_SIDES = 8; const int DEFAULT_WIDTH = 100; const int DEFAULT_SCALE = 100; //const int DEFAULT_SHAPE = 10; m_layout = boxLayout(); m_segmentsLabel = new QLabel( tr("Segments"), mainWidget() ); m_layout->addWidget( m_segmentsLabel ); m_segmentsValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_segmentsValue ); m_segmentsValue->setMinimum( 1 ); m_segmentsValue->setMaximum( 100 ); int segmentsVal = DEFAULT_SEGMENTS; if ( g_prefs.exists( "ui_cylindertool_segments" ) ) { int val = g_prefs( "ui_cylindertool_segments" ).intValue(); if ( val >= 1 && val <= 100 ) { segmentsVal = val; } } m_segmentsValue->setValue( segmentsVal ); m_sidesLabel = new QLabel( tr("Sides"), mainWidget() ); m_layout->addWidget( m_sidesLabel ); m_sidesValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_sidesValue ); m_sidesValue->setMinimum( 3 ); m_sidesValue->setMaximum( 100 ); int sidesVal = DEFAULT_SIDES; if ( g_prefs.exists( "ui_cylindertool_sides" ) ) { int val = g_prefs( "ui_cylindertool_sides" ).intValue(); if ( val >= 3 && val <= 100 ) { sidesVal = val; } } m_sidesValue->setValue( sidesVal ); m_widthLabel = new QLabel( tr("Width"), mainWidget() ); m_layout->addWidget( m_widthLabel ); m_widthValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_widthValue ); m_widthValue->setMinimum( 0 ); m_widthValue->setMaximum( 100 ); int widthVal = DEFAULT_WIDTH; if ( g_prefs.exists( "ui_cylindertool_width" ) ) { int val = g_prefs( "ui_cylindertool_width" ).intValue(); if ( val >= 0 && val <= 100 ) { widthVal = val; } } m_widthValue->setValue( widthVal ); m_scaleLabel = new QLabel( tr("Scale"), mainWidget() ); m_layout->addWidget( m_scaleLabel ); m_scaleValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_scaleValue ); m_scaleValue->setMinimum( 0 ); m_scaleValue->setMaximum( 100 ); int scaleVal = DEFAULT_SCALE; if ( g_prefs.exists( "ui_cylindertool_scale" ) ) { int val = g_prefs( "ui_cylindertool_scale" ).intValue(); if ( val >= 0 && val <= 100 ) { scaleVal = val; } } m_scaleValue->setValue( scaleVal ); /* m_shapeLabel = new QLabel( "Shape", mainWidget() ); m_layout->addWidget( m_shapeLabel ); m_shapeValue = new QSlider( Qt::Horizontal, mainWidget() ); m_layout->addWidget( m_shapeValue ); m_shapeValue->setMinimumWidth( 64 ); m_shapeValue->setMinimum( 0 ); m_shapeValue->setMaximum( DEFAULT_SHAPE * 2 ); m_shapeValue->setValue( DEFAULT_SHAPE ); m_shapeValue->setTickInterval( DEFAULT_SHAPE / 2); m_shapeValue->setTickmarks( QSlider::Below ); */ m_layout->addStretch(); connect( m_segmentsValue, SIGNAL(valueChanged(int)), this, SLOT(segmentsValueChanged(int)) ); connect( m_sidesValue, SIGNAL(valueChanged(int)), this, SLOT(sidesValueChanged(int)) ); connect( m_widthValue, SIGNAL(valueChanged(int)), this, SLOT(widthValueChanged(int)) ); connect( m_scaleValue, SIGNAL(valueChanged(int)), this, SLOT(scaleValueChanged(int)) ); //connect( m_shapeValue, SIGNAL(valueChanged(int)), this, SLOT(shapeValueChanged(int)) ); m_segmentsLabel->show(); m_segmentsValue->show(); m_sidesLabel->show(); m_sidesValue->show(); m_widthLabel->show(); m_widthValue->show(); m_scaleLabel->show(); m_scaleValue->show(); //m_shapeLabel->hide(); //m_shapeValue->hide(); segmentsValueChanged( segmentsVal ); sidesValueChanged( sidesVal ); widthValueChanged( widthVal ); scaleValueChanged( scaleVal ); //shapeValueChanged( DEFAULT_SHAPE ); } CylinderToolWidget::~CylinderToolWidget() { } void CylinderToolWidget::segmentsValueChanged( int newValue ) { g_prefs( "ui_cylindertool_segments" ) = newValue; m_observer->setSegmentsValue( newValue ); } void CylinderToolWidget::sidesValueChanged( int newValue ) { g_prefs( "ui_cylindertool_sides" ) = newValue; m_observer->setSidesValue( newValue ); } void CylinderToolWidget::widthValueChanged( int newValue ) { g_prefs( "ui_cylindertool_width" ) = newValue; m_observer->setWidthValue( newValue ); } void CylinderToolWidget::scaleValueChanged( int newValue ) { g_prefs( "ui_cylindertool_scale" ) = newValue; m_observer->setScaleValue( newValue ); } void CylinderToolWidget::shapeValueChanged( int newValue ) { m_observer->setShapeValue( newValue ); } mm3d-1.3.15/src/tools/cylindertoolwidget.h000066400000000000000000000045701466047437300205000ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __CYLINDERTOOLWIDGET_H #define __CYLINDERTOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QSpinBox; class QSlider; class QLabel; #include "toolwidget.h" class CylinderToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setSegmentsValue( int newValue ) = 0; virtual void setSidesValue( int newValue ) = 0; virtual void setWidthValue( int newValue ) = 0; virtual void setScaleValue( int newValue ) = 0; virtual void setShapeValue( int newValue ) = 0; }; CylinderToolWidget( Observer * observer, QMainWindow * parent ); virtual ~CylinderToolWidget(); public slots: void segmentsValueChanged( int newValue ); void sidesValueChanged( int newValue ); void widthValueChanged( int newValue ); void scaleValueChanged( int newValue ); void shapeValueChanged( int newValue ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_segmentsLabel; QSpinBox * m_segmentsValue; QLabel * m_sidesLabel; QSpinBox * m_sidesValue; QLabel * m_widthLabel; QSpinBox * m_widthValue; QLabel * m_scaleLabel; QSpinBox * m_scaleValue; //QLabel * m_shapeLabel; //QSlider * m_shapeValue; }; #endif // __CYLINDERTOOLWIDGET_H mm3d-1.3.15/src/tools/dragvertextool.cc000066400000000000000000000140011466047437300177620ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "dragvertextool.h" #include "pixmap/dragvertextool.xpm" #include "glmath.h" #include "model.h" #include "modelstatus.h" #include #include #include #include "log.h" DragVertexTool::DragVertexTool() { } DragVertexTool::~DragVertexTool() { } void DragVertexTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); m_vertVectors.clear(); std::list vertices; model->getSelectedVertices( vertices ); if ( !vertices.empty() ) { m_vertId = vertices.front(); m_view = parent->getParentViewMatrix(); // model to viewport space m_inv = parent->getParentViewInverseMatrix(); // viewport to model space m_vertCoords[0] = 0; m_vertCoords[1] = 0; m_vertCoords[2] = 0; model->getVertexCoords( m_vertId, m_vertCoords ); log_debug( "vertex %d is at ( %f, %f, %f )\n", m_vertId, m_vertCoords[0], m_vertCoords[1], m_vertCoords[2] ); m_view.apply3x( m_vertCoords ); size_t tcount = model->getTriangleCount(); for ( size_t t = 0; t < tcount; t++ ) { int tv[3]; int i; bool match = false; for ( i = 0; i < 3; i++ ) { tv[i] = model->getTriangleVertex( t, i ); if ( tv[i] == m_vertId ) { // TODO: could find the same vertex multiple times if // edge belongs to more than one triangle, may want // to prevent that in the future match = true; } } if ( match ) { for ( i = 0; i < 3; i++ ) { if ( tv[i] != m_vertId ) { double c[3] = { 0, 0, 0 }; model->getVertexCoords( tv[i], c ); m_view.apply3x( c ); Vector v; v[0] = c[0] - m_vertCoords[0]; v[1] = c[1] - m_vertCoords[1]; v[2] = c[2] - m_vertCoords[2]; log_debug( "adding vector ( %f, %f, %f )\n", v[0], v[1], v[2] ); m_vertVectors.push_back( v ); } } } } model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Dragging selected vertex" ).toUtf8().data() ); } else { m_vertId = -1; model_status( parent->getModel(), StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Tool", "Must a vertex selected" ).toUtf8().data() ); } } void DragVertexTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Drag complete" ).toUtf8().data() ); } void DragVertexTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { Matrix m; Vector newPos; parent->getParentXYValue( x, y, newPos[0], newPos[1] ); Model * model = parent->getModel(); log_debug( "pos is ( %f, %f, %f )\n", newPos[0], newPos[1], newPos[2] ); newPos[0] -= m_vertCoords[0]; newPos[1] -= m_vertCoords[1]; log_debug( "pos vector is ( %f, %f, %f )\n", newPos[0], newPos[1], newPos[2] ); double pscale = newPos.mag3(); newPos.normalize3(); log_debug( "mouse has moved %f units\n", pscale ); if ( !m_vertVectors.empty() && m_vertId >= 0 ) { Vector best = m_vertVectors.front(); double bestDist = 0.0; double ratio = 1.0; std::list< Vector >::iterator it; for ( it = m_vertVectors.begin(); it != m_vertVectors.end(); it++ ) { Vector vtry = (*it); vtry[2] = 0; double trylen = vtry.mag3(); vtry.normalize3(); double d = newPos.dot3( vtry ); log_debug( " dot3 is %f (%f, %f, %f)\n", d, vtry[0], vtry[1], vtry[2] ); if ( fabs( d ) > fabs( bestDist ) ) { bestDist = d; best = (*it); ratio = pscale / trylen; } } log_debug( "best vector is (%f, %f, %f)\n", best[0], best[1], best[2] ); best.scale3( bestDist * ratio ); log_debug( "best scaled is (%f, %f, %f)\n", best[0], best[1], best[2] ); best[0] += m_vertCoords[0]; best[1] += m_vertCoords[1]; best[2] += m_vertCoords[2]; log_debug( "best sum is (%f, %f, %f)\n", best[0], best[1], best[2] ); m_inv.apply3x( best ); log_debug( "best applied is (%f, %f, %f)\n", best[0], best[1], best[2] ); model->moveVertex( m_vertId, best[0], best[1], best[2] ); } parent->updateAllViews(); } const char ** DragVertexTool::getPixmap() { return (const char **) dragvertextool_xpm; } void DragVertexTool::activated( int argc, Model * model, QMainWindow * mainwin ) { //model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Tip: Hold shift to restrict movement to one dimension" ).toUtf8().data() ); } const char * DragVertexTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Drag Vertex on Edge" ); } mm3d-1.3.15/src/tools/dragvertextool.h000066400000000000000000000035331466047437300176340ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __DRAGVERTEXTOOL_H #define __DRAGVERTEXTOOL_H #include "tool.h" #include "glmath.h" #include #include class DragVertexTool: public Tool { public: DragVertexTool(); virtual ~DragVertexTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_M; return true; }; void activated( int argc, Model * model, QMainWindow * mainwin ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: Matrix m_view; Matrix m_inv; int m_vertId; double m_vertCoords[3]; std::list< Vector > m_vertVectors; }; #endif // __DRAGVERTEXTOOL_H mm3d-1.3.15/src/tools/ellipsetool.cc000066400000000000000000000160051466047437300172520ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "ellipsetool.h" #include "pixmap/ellipsetool.xpm" #include "model.h" #include "glmath.h" #include "log.h" #include "modelstatus.h" #include #include #include #include using std::vector; using std::list; EllipsoidTool::EllipsoidTool() : m_smoothness( 2 ), m_isSphere( false ), m_fromCenter( false ) { } EllipsoidTool::~EllipsoidTool() { } void EllipsoidTool::activated( int arg, Model * model, QMainWindow * mainwin ) { log_debug( "ellipse activated\n" ); m_widget = new EllipsoidToolWidget( this, mainwin ); m_widget->show(); } void EllipsoidTool::deactivated() { m_widget->close(); } void EllipsoidTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); model->unselectAll(); // create rough 12-vertex sphere // Make top and bottom vertices ToolCoordT v1 = addPosition( parent, Model::PT_Vertex, NULL, 0, 1, 0 ); ToolCoordT v2 = addPosition( parent, Model::PT_Vertex, NULL, 0, -1, 0 ); // Make center vertices double offset = sin( 30 * PIOVER180 ); double adjust = cos( 30 * PIOVER180 ); vector top; vector bot; ToolCoordT v; double xoff; double zoff; for ( int t = 0; t < 5; t++ ) { // upper vertex xoff = sin ( ( t * 72) * PIOVER180 ) * adjust; zoff = cos ( ( t * 72) * PIOVER180 ) * adjust; v = addPosition( parent, Model::PT_Vertex, NULL, xoff, offset, zoff ); top.push_back( v.pos.index ); // lower vertex xoff = sin ( ( t * 72 + 36) * PIOVER180 ) * adjust; zoff = cos ( ( t * 72 + 36) * PIOVER180 ) * adjust; v = addPosition( parent, Model::PT_Vertex, NULL, xoff, -offset, zoff ); bot.push_back( v.pos.index ); } // Create top and bottom faces model->selectTriangle( model->addTriangle( v1.pos.index, top.back(), top.front() ) ); model->selectTriangle( model->addTriangle( v2.pos.index, bot.front(), bot.back() ) ); vector::iterator it1; vector::iterator it2; vector::reverse_iterator rit1; vector::reverse_iterator rit2; for ( it1 = top.begin(); ; it1++ ) { it2 = it1; it2++; if ( it2 == top.end() ) { break; } model->selectTriangle( model->addTriangle( v1.pos.index, *it1, *it2 ) ); } for ( rit1 = bot.rbegin(); ; rit1++ ) { rit2 = rit1; rit2++; if ( rit2 == bot.rend() ) { break; } model->selectTriangle( model->addTriangle( v2.pos.index, *rit1, *rit2 ) ); } model->selectTriangle( model->addTriangle( top[0], bot[0], top[1] ) ); model->selectTriangle( model->addTriangle( top[1], bot[1], top[2] ) ); model->selectTriangle( model->addTriangle( top[2], bot[2], top[3] ) ); model->selectTriangle( model->addTriangle( top[3], bot[3], top[4] ) ); model->selectTriangle( model->addTriangle( top[4], bot[4], top[0] ) ); model->selectTriangle( model->addTriangle( bot[1], top[1], bot[0] ) ); model->selectTriangle( model->addTriangle( bot[2], top[2], bot[1] ) ); model->selectTriangle( model->addTriangle( bot[3], top[3], bot[2] ) ); model->selectTriangle( model->addTriangle( bot[4], top[4], bot[3] ) ); model->selectTriangle( model->addTriangle( bot[0], top[0], bot[4] ) ); // create smooth sphere for ( unsigned i = 0; i < m_smoothness; i++ ) { model->subdivideSelectedTriangles(); } list posList; model->getSelectedPositions( posList ); m_vertices.clear(); // TODO add makeSelectedToolCoordList? makeToolCoordList( parent, m_vertices, posList ); ToolCoordList::iterator vit; for ( vit = m_vertices.begin(); vit != m_vertices.end(); vit++ ) { normalize3( (*vit).oldCoords ); } double pos[3] = {0,0,0}; double rad[3] = {0,0,0}; parent->getParentXYValue( x, y, pos[0], pos[1], true ); m_startX = pos[0]; m_startY = pos[1]; updateVertexCoords( parent, pos[0], pos[1], pos[2], rad[0], rad[1], rad[2] ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Ellipsoid created" ).toUtf8().data() ); } void EllipsoidTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { m_vertices.clear(); } void EllipsoidTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { double pos[3] = {0,0,0}; double rad[3] = {0,0,0}; parent->getParentXYValue( x, y, pos[0], pos[1] ); if ( m_isSphere ) { double sphereRadius = 0; double a, b; a = fabs((m_startX - pos[0]) / 2); b = fabs((m_startY - pos[1]) / 2); sphereRadius = (a < b) ? a : b; rad[0] = rad[1] = rad[2] = sphereRadius; } else { rad[0] = fabs(m_startX - pos[0]) / 2; rad[1] = fabs(m_startY - pos[1]) / 2; rad[2] = ( rad[0] < rad[1] ) ? rad[0] : rad[1]; } if ( m_fromCenter ) { pos[0] = m_startX; pos[1] = m_startY; rad[0] *= 2.0; rad[1] *= 2.0; rad[2] *= 2.0; } else { pos[0] = (pos[0] + m_startX) / 2; pos[1] = (pos[1] + m_startY) / 2; } updateVertexCoords( parent, pos[0], pos[1], pos[2], rad[0], rad[1], rad[2] ); parent->updateAllViews(); } const char ** EllipsoidTool::getPixmap() { return (const char **) ellipsetool_xpm; } void EllipsoidTool::updateVertexCoords( Tool::Parent * parent, double x, double y, double z, double xrad, double yrad, double zrad ) { ToolCoordList::iterator it; // TODO it would probably be considerably faster to copy the matrix // logic into this function for ( it = m_vertices.begin(); it != m_vertices.end(); it++ ) { movePosition( parent, (*it).pos, (*it).oldCoords[0]*xrad + x, (*it).oldCoords[1]*yrad + y, (*it).oldCoords[2]*zrad + z ); } } void EllipsoidTool::setSmoothnessValue( int newValue ) { m_smoothness = newValue; } void EllipsoidTool::setSphere( bool o ) { m_isSphere = o; } void EllipsoidTool::setCenter( bool o ) { m_fromCenter = o; } const char * EllipsoidTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Ellipsoid" ); } mm3d-1.3.15/src/tools/ellipsetool.h000066400000000000000000000041371466047437300171170ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ELLIPSOIDTOOL_H #define __ELLIPSOIDTOOL_H #include "tool.h" #include "ellipsetoolwidget.h" #include class EllipsoidTool : public Tool, public EllipsoidToolWidget::Observer { public: EllipsoidTool(); virtual ~EllipsoidTool(); int getToolCount() { return 1; }; const char * getName( int arg ); void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // EllipsoidToolWidget::Observer void setSmoothnessValue( int newValue ); void setSphere( bool o ); void setCenter( bool o ); protected: void updateVertexCoords( Tool::Parent *, double x, double y, double z, double xrad, double yrad, double zrad ); EllipsoidToolWidget * m_widget; unsigned m_smoothness; bool m_isSphere; bool m_fromCenter; ToolCoordList m_vertices; double m_startX; double m_startY; }; #endif // __ELLIPSOIDTOOL_H mm3d-1.3.15/src/tools/ellipsetoolwidget.cc000066400000000000000000000101311466047437300204500ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "ellipsetoolwidget.h" #include #include #include #include #include #include #include #include #include "3dmprefs.h" EllipsoidToolWidget::EllipsoidToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const int DEFAULT_SMOOTHNESS = 2; const bool DEFAULT_SPHERE = false; const bool DEFAULT_CENTER = false; m_layout = boxLayout(); m_smoothLabel = new QLabel( tr("Smoothness:"), mainWidget() ); m_layout->addWidget( m_smoothLabel ); m_smoothValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_smoothValue ); m_smoothValue->setMinimum( 0 ); m_smoothValue->setMaximum( 5 ); m_facesLabel = new QLabel( tr("Faces: ") + QString("320"), mainWidget() ); m_layout->addWidget( m_facesLabel ); m_sphereCheckBox = new QCheckBox( tr("Sphere"), mainWidget() ); m_layout->addWidget( m_sphereCheckBox ); m_centerCheckBox = new QCheckBox( tr("From Center", "Checkbox that indicates if ellipsoid is created from center or far corner"), mainWidget() ); m_layout->addWidget( m_centerCheckBox ); int smoothVal = DEFAULT_SMOOTHNESS; g_prefs.setDefault( "ui_ellipsetool_smoothness", DEFAULT_SMOOTHNESS ); int val = g_prefs( "ui_ellipsetool_smoothness" ).intValue(); if ( val >= 0 && val <= 5 ) { smoothVal = val; } m_smoothValue->setValue( smoothVal ); g_prefs.setDefault( "ui_ellipsetool_issphere", DEFAULT_SPHERE ? 1 : 0 ); bool isSphere = DEFAULT_SPHERE; isSphere = (g_prefs( "ui_ellipsetool_issphere" ).intValue() != 0) ? true : false; m_sphereCheckBox->setChecked( isSphere ); g_prefs.setDefault( "ui_ellipsetool_fromcenter", DEFAULT_CENTER ? 1 : 0 ); bool fromCenter = DEFAULT_CENTER; fromCenter = (g_prefs( "ui_ellipsetool_fromcenter" ).intValue() != 0) ? true : false; m_centerCheckBox->setChecked( fromCenter ); m_layout->addStretch(); connect( m_smoothValue, SIGNAL(valueChanged(int)), this, SLOT(smoothnessValueChanged(int)) ); connect( m_sphereCheckBox, SIGNAL(toggled(bool)), this, SLOT(sphereCheckBoxValueChanged(bool)) ); connect( m_centerCheckBox, SIGNAL(toggled(bool)), this, SLOT(centerCheckBoxValueChanged(bool)) ); m_smoothLabel->show(); m_smoothValue->show(); m_facesLabel->show(); m_sphereCheckBox->show(); m_centerCheckBox->show(); smoothnessValueChanged( smoothVal ); sphereCheckBoxValueChanged( isSphere ); centerCheckBoxValueChanged( fromCenter ); } EllipsoidToolWidget::~EllipsoidToolWidget() { } void EllipsoidToolWidget::smoothnessValueChanged( int newValue ) { QString str = tr("Faces: "); str += QString::number( 20 * (unsigned) pow(4, newValue) ); m_facesLabel->setText( str ); g_prefs( "ui_ellipsetool_smoothness" ) = newValue; m_observer->setSmoothnessValue( newValue ); } void EllipsoidToolWidget::sphereCheckBoxValueChanged( bool o ) { g_prefs( "ui_ellipsetool_issphere" ) = o ? 1 : 0; m_observer->setSphere( o ); } void EllipsoidToolWidget::centerCheckBoxValueChanged( bool o ) { g_prefs( "ui_ellipsetool_fromcenter" ) = o ? 1 : 0; m_observer->setCenter( o ); } mm3d-1.3.15/src/tools/ellipsetoolwidget.h000066400000000000000000000037461466047437300203300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ELLIPSETOOLWIDGET_H #define __ELLIPSETOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QSpinBox; class QLabel; class QCheckBox; #include "toolwidget.h" class EllipsoidToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setSmoothnessValue( int newValue ) = 0; virtual void setSphere( bool o ) = 0; virtual void setCenter( bool o ) = 0; }; EllipsoidToolWidget( Observer * observer, QMainWindow * parent ); virtual ~EllipsoidToolWidget(); public slots: void smoothnessValueChanged( int newValue ); void sphereCheckBoxValueChanged( bool o ); void centerCheckBoxValueChanged( bool o ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_smoothLabel; QSpinBox * m_smoothValue; QLabel * m_facesLabel; QCheckBox * m_sphereCheckBox; QCheckBox * m_centerCheckBox; }; #endif // __ELLIPSETOOLWIDGET_H mm3d-1.3.15/src/tools/extrudetool.cc000066400000000000000000000177561466047437300173130ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "extrudetool.h" #include "pixmap/extrudetool.xpm" #include "glmath.h" #include "model.h" #include "modelstatus.h" #include #include #include #include "log.h" ExtrudeTool::ExtrudeTool() { } ExtrudeTool::~ExtrudeTool() { } void ExtrudeTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_x = 0.0; m_y = 0.0; parent->getParentXYValue( x, y, m_x, m_y, true ); m_allowX = true; m_allowY = true; m_viewInverse = parent->getParentViewInverseMatrix(); m_model = parent->getModel(); extrudeEvent(); } void ExtrudeTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { /* Model * model = parent->getModel(); unsigned pcount = model->getProjectionCount(); for ( unsigned p = 0; p < pcount; p++ ) { if ( model->isProjectionSelected( p ) ) { model->applyProjection(p); } } */ parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Extrude complete" ).toUtf8().data() ); } void ExtrudeTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { Matrix m; double x2 = m_x; double y2 = m_y; parent->getParentXYValue( x, y, x2, y2 ); if ( buttonState & BS_Shift ) { if ( m_allowX && m_allowY ) { double ax = fabs( x2 - m_x ); double ay = fabs( y2 - m_y ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { x2 = m_x; } if ( !m_allowY ) { y2 = m_y; } double v[4] = { x2 - m_x, y2 - m_y, 0.0, 1.0 }; m_x = x2; m_y = y2; m_viewInverse.apply3( v ); m.set( 3, 0, v[0] ); m.set( 3, 1, v[1] ); m.set( 3, 2, v[2] ); parent->getModel()->translateSelected( m ); parent->updateAllViews(); } const char ** ExtrudeTool::getPixmap() { return (const char **) extrudetool_xpm; } void ExtrudeTool::activated( int argc, Model * model, QMainWindow * mainwin ) { model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Tip: Hold shift to restrict movement to one dimension" ).toUtf8().data() ); } const char * ExtrudeTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Extrude" ); } // Extrude methods void ExtrudeTool::extrudeEvent() { m_sides.clear(); m_evMap.clear(); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Extrude complete").toUtf8().data() ); list faces; m_model->getSelectedTriangles( faces ); list vertices; m_model->getSelectedVertices( vertices ); if ( faces.empty() ) { model_status( m_model, StatusError, STATUSTIME_LONG, "%s", qApp->translate( "Tool", "Must have faces selected to extrude" ).toUtf8().data() ); } else { // Find edges (sides with count==1) list::iterator it; for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = m_model->getTriangleVertex( *it, t ); } for ( int t = 0; t < (3 - 1); t++ ) { addSide( v[t], v[t+1] ); } addSide( v[0], v[2] ); } // make extruded vertices and create a map from old vertices // to new vertices for ( it = vertices.begin(); it != vertices.end(); it++ ) { double coord[3]; m_model->getVertexCoords( *it, coord ); unsigned i = m_model->addVertex( coord[0], coord[1], coord[2] ); m_evMap[*it] = i; log_debug( "added vertex %d for %d at %f,%f,%f\n", m_evMap[*it], *it, coord[0], coord[1], coord[2] ); } // Add faces for edges for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned v[3]; for ( int t = 0; t < 3; t++ ) { v[t] = m_model->getTriangleVertex( *it, t ); } for ( int t = 0; t < (3 - 1); t++ ) { if ( sideIsEdge( v[t], v[t+1] ) ) { makeFaces( v[t], v[t+1] ); } } if ( sideIsEdge( v[2], v[0] ) ) { makeFaces( v[2], v[0] ); } } // Map selected faces onto extruded vertices for ( it = faces.begin(); it != faces.end(); it++ ) { unsigned tri = *it; int v1 = m_model->getTriangleVertex( tri, 0 ); int v2 = m_model->getTriangleVertex( tri, 1 ); int v3 = m_model->getTriangleVertex( tri, 2 ); /* // TODO widget for back-facing if ( m_backFaceCheckbox->isChecked() ) { int newTri = m_model->addTriangle( v1, v2, v3 ); m_model->invertNormals( newTri ); } */ log_debug( "face %d uses vertices %d,%d,%d\n", *it, v1, v2, v3 ); m_model->setTriangleVertices( tri, m_evMap[ m_model->getTriangleVertex( tri, 0 ) ], m_evMap[ m_model->getTriangleVertex( tri, 1 ) ], m_evMap[ m_model->getTriangleVertex( tri, 2 ) ] ); log_debug( "moved face %d to vertices %d,%d,%d\n", *it, m_model->getTriangleVertex( tri, 0 ), m_model->getTriangleVertex( tri, 1 ), m_model->getTriangleVertex( tri, 2 ) ); } // Update face selection ExtrudedVertexMap::iterator evit; for ( evit = m_evMap.begin(); evit != m_evMap.end(); evit++ ) { m_model->unselectVertex( (*evit).first ); m_model->selectVertex( (*evit).second ); } m_model->deleteOrphanedVertices(); model_status( m_model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Extruding selected faces" ).toUtf8().data() ); } } void ExtrudeTool::makeFaces( unsigned a, unsigned b ) { unsigned a2 = m_evMap[a]; unsigned b2 = m_evMap[b]; m_model->addTriangle( b, b2, a2 ); m_model->addTriangle( a2, a, b ); } void ExtrudeTool::addSide( unsigned a, unsigned b ) { // Make sure a < b to simplify comparison below if ( b < a ) { unsigned c = a; a = b; b = c; } // Find existing side (if any) and increment count SideList::iterator it; for ( it = m_sides.begin(); it != m_sides.end(); it++ ) { if ( (*it).a == a && (*it).b == b ) { (*it).count++; log_debug( "side (%d,%d) = %d\n", a, b, (*it).count ); return; } } // Not found, add new side with a count of 1 SideT s; s.a = a; s.b = b; s.count = 1; log_debug( "side (%d,%d) = %d\n", a, b, s.count ); m_sides.push_back( s ); } bool ExtrudeTool::sideIsEdge( unsigned a, unsigned b ) { // Make sure a < b to simplify comparison below if ( b < a ) { unsigned c = a; a = b; b = c; } SideList::iterator it; for ( it = m_sides.begin(); it != m_sides.end(); it++ ) { if ( (*it).a == a && (*it).b == b ) { if ( (*it).count == 1 ) { return true; } else { return false; } } } return false; } mm3d-1.3.15/src/tools/extrudetool.h000066400000000000000000000045031466047437300171370ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __EXTRUDETOOL_H #define __EXTRUDETOOL_H #include "tool.h" #include #include #include class ExtrudeTool: public Tool { public: ExtrudeTool(); virtual ~ExtrudeTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_M; return true; }; void activated( int argc, Model * model, QMainWindow * mainwin ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: // Extrude methods void extrudeEvent(); void makeFaces( unsigned a, unsigned b ); void addSide( unsigned a, unsigned b ); bool sideIsEdge( unsigned a, unsigned b ); // Extrude data struct _Side_t { unsigned a; unsigned b; int count; }; typedef struct _Side_t SideT; typedef std::list SideList; typedef std::map ExtrudedVertexMap; SideList m_sides; ExtrudedVertexMap m_evMap; // Tool data Model * m_model; Matrix m_viewInverse; double m_x; double m_y; bool m_allowX; bool m_allowY; }; #endif // __EXTRUDETOOL_H mm3d-1.3.15/src/tools/jointtool.cc000066400000000000000000000072311466047437300167410ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "jointtool.h" #include "model.h" #include "msg.h" #include "log.h" #include "modelstatus.h" #include "pixmap/jointtool.xpm" #include #include JointTool::JointTool() { m_joint.pos.type = Model::PT_Point; } JointTool::~JointTool() { } void JointTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); double coord[3] = {0,0,0}; parent->getParentXYValue( x, y, coord[0], coord[1], true ); const Matrix & viewMatrix = parent->getParentViewMatrix(); int p = -1; double pDist = 0.0; double parentCoords[4]; int jointCount = model->getBoneJointCount(); for ( int t = 0; t < jointCount; t++ ) { model->getBoneJointCoords( t, parentCoords ); parentCoords[3] = 1; viewMatrix.apply( parentCoords ); double dist = distance( coord[0], coord[1], parentCoords[0], parentCoords[1] ); if ( p == -1 || dist < pDist ) { p = t; pDist = dist; } } // Find a unique name for the joint char name[32] = "Joint 1"; unsigned c = model->getBoneJointCount(); bool uniqueName = (c == 0) ? true : false; for ( unsigned i = 1; !uniqueName && i < 1000; i++ ) { uniqueName = true; sprintf( name, "Joint %d", i ); for ( unsigned j = 0; j < c; j++ ) { if ( strcmp( name, model->getBoneJointName( j ) ) == 0 ) { uniqueName = false; break; } } } // I give up, just call it "Joint" if ( ! uniqueName ) { strcpy( name, "Joint" ); } m_joint = addPosition( parent, Model::PT_Joint, name, coord[0], coord[1], coord[2], 0, 0, 0, p ); model->unselectAll(); model->selectBoneJoint( m_joint.pos.index ); parent->updateAllViews(); if ( p >= 0 ) { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Joint created" ).toUtf8().data() ); } else { model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Root joint created" ).toUtf8().data() ); } } void JointTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_joint.pos.type == Model::PT_Joint ) { double coord[3] = {0,0,0}; parent->getParentXYValue( x, y, coord[0], coord[1] ); movePosition( parent, m_joint.pos, coord[0], coord[1], coord[2] ); parent->updateAllViews(); } } void JointTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { } const char ** JointTool::getPixmap() { return (const char **) jointtool_xpm; } const char * JointTool::getPath() { return TOOLS_CREATE_MENU; } const char * JointTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Bone Joint" ); } mm3d-1.3.15/src/tools/jointtool.h000066400000000000000000000027441466047437300166070ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __JOINTTOOL_H #define __JOINTTOOL_H #include "tool.h" class JointTool : public Tool { public: JointTool(); virtual ~JointTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: ToolCoordT m_joint; }; #endif // __JOINTTOOL_H mm3d-1.3.15/src/tools/movetool.cc000066400000000000000000000064041466047437300165650ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "movetool.h" #include "pixmap/movetool.xpm" #include "glmath.h" #include "model.h" #include "modelstatus.h" #include #include #include #include "log.h" MoveTool::MoveTool() { } MoveTool::~MoveTool() { } void MoveTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_x = 0.0; m_y = 0.0; parent->getParentXYValue( x, y, m_x, m_y, true ); m_allowX = true; m_allowY = true; m_viewInverse = parent->getParentViewInverseMatrix(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Moving selected primitives" ).toUtf8().data() ); } void MoveTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { /* Model * model = parent->getModel(); unsigned pcount = model->getProjectionCount(); for ( unsigned p = 0; p < pcount; p++ ) { if ( model->isProjectionSelected( p ) ) { model->applyProjection(p); } } */ parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Move complete" ).toUtf8().data() ); } void MoveTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { Matrix m; double x2 = m_x; double y2 = m_y; parent->getParentXYValue( x, y, x2, y2 ); if ( buttonState & BS_Shift ) { if ( m_allowX && m_allowY ) { double ax = fabs( x2 - m_x ); double ay = fabs( y2 - m_y ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { x2 = m_x; } if ( !m_allowY ) { y2 = m_y; } double v[4] = { x2 - m_x, y2 - m_y, 0.0, 1.0 }; m_x = x2; m_y = y2; m_viewInverse.apply3( v ); m.set( 3, 0, v[0] ); m.set( 3, 1, v[1] ); m.set( 3, 2, v[2] ); parent->getModel()->translateSelected( m ); parent->updateAllViews(); } const char ** MoveTool::getPixmap() { return (const char **) movetool_xpm; } void MoveTool::activated( int argc, Model * model, QMainWindow * mainwin ) { model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Tip: Hold shift to restrict movement to one dimension" ).toUtf8().data() ); } const char * MoveTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Move" ); } mm3d-1.3.15/src/tools/movetool.h000066400000000000000000000033361466047437300164300ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __MOVETOOL_H #define __MOVETOOL_H #include "tool.h" #include class MoveTool: public Tool { public: MoveTool(); virtual ~MoveTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_M; return true; }; void activated( int argc, Model * model, QMainWindow * mainwin ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: Matrix m_viewInverse; double m_x; double m_y; bool m_allowX; bool m_allowY; }; #endif // __MOVETOOL_H mm3d-1.3.15/src/tools/pointtool.cc000066400000000000000000000057021466047437300167500ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "pointtool.h" #include "model.h" #include "msg.h" #include "log.h" #include "modelstatus.h" #include "pixmap/pointtool.xpm" #include #include PointTool::PointTool() { m_point.pos.type = Model::PT_Vertex; } PointTool::~PointTool() { } void PointTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); double coord[3] = {0,0,0}; parent->getParentXYValue( x, y, coord[0], coord[1], true ); // Find a unique name for the point char name[32] = "Point 1"; unsigned c = model->getPointCount(); bool uniqueName = (c == 0) ? true : false; for ( unsigned i = 1; !uniqueName && i < 1000; i++ ) { uniqueName = true; sprintf( name, "Point %d", i ); for ( unsigned j = 0; j < c; j++ ) { if ( strcmp( name, model->getPointName( j ) ) == 0 ) { uniqueName = false; break; } } } // I give up, just call it "Point" if ( ! uniqueName ) { strcpy( name, "Point" ); } m_point = addPosition( parent, Model::PT_Point, name, coord[0], coord[1], coord[2], 0, 0, 0 ); model->unselectAll(); model->selectPoint( m_point.pos.index ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Point created" ).toUtf8().data() ); } void PointTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_point.pos.type == Model::PT_Point ) { double coord[3] = {0,0,0}; parent->getParentXYValue( x, y, coord[0], coord[1] ); movePosition( parent, m_point.pos, coord[0], coord[1], coord[2] ); parent->updateAllViews(); } } void PointTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { } const char ** PointTool::getPixmap() { return (const char **) pointtool_xpm; } const char * PointTool::getPath() { return TOOLS_CREATE_MENU; } const char * PointTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Point" ); } mm3d-1.3.15/src/tools/pointtool.h000066400000000000000000000027441466047437300166150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __POINTTOOL_H #define __POINTTOOL_H #include "tool.h" class PointTool : public Tool { public: PointTool(); virtual ~PointTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: ToolCoordT m_point; }; #endif // __POINTTOOL_H mm3d-1.3.15/src/tools/polytool.cc000066400000000000000000000122551466047437300166030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "polytool.h" #include "model.h" #include "msg.h" #include "log.h" #include "modelstatus.h" #include "pixmap/polytool.xpm" #include #include PolyTool::PolyTool() : m_model( NULL ), m_type( 0 ), m_widget( NULL ) { m_lastVertex.pos.type = Model::PT_Point; m_lastVertex.pos.index = 0; } PolyTool::~PolyTool() { } void PolyTool::activated( int arg, Model * model, QMainWindow * mainwin ) { m_model = model; m_widget = new PolyToolWidget( this, mainwin ); m_widget->show(); } void PolyTool::deactivated() { m_model->deleteOrphanedVertices(); m_widget->close(); } void PolyTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); m_model = model; if ( buttonState == BS_Left ) { m_verts.clear(); IntList selected; model->getSelectedVertices( selected ); IntList::iterator it; for ( it = selected.begin(); m_verts.size() < 3 && it != selected.end(); it++ ) { m_verts.push_back( *it ); } double coord[3] = {0,0,0}; // Technically that last argument could be true, I chose false // because for the poly tool you'd end up creating a flat triangle // that would just get deleted parent->getParentXYValue( x, y, coord[0], coord[1], false ); m_lastVertex = addPosition( parent, Model::PT_Vertex, NULL, coord[0], coord[1], coord[2] ); if ( m_verts.size() == 3 ) { if ( m_type == 0 ) { // Fan int v = m_verts.front(); model->unselectVertex( v ); m_verts.pop_front(); m_verts.push_back( m_lastVertex.pos.index ); } else { // Strip it = m_verts.begin(); it++; int v = *it; m_verts.erase( it ); model->unselectVertex( v ); m_verts.push_back( m_lastVertex.pos.index ); } } else { m_verts.push_back( m_lastVertex.pos.index ); } if ( m_verts.size() == 3 ) { it = m_verts.begin(); int v1 = *it; it++; int v2 = *it; it++; int v3 = *it; it++; int t = model->addTriangle( v1, v2, v3 ); model->beginSelectionDifference(); //model->unselectAllTriangles(); model->selectVertex( m_lastVertex.pos.index ); //model->selectTriangle( t ); const Matrix & viewMatrix = parent->getParentViewInverseMatrix(); Vector viewNorm( 0, 0, 1 ); viewMatrix.show(); viewNorm.transform3( viewMatrix ); float norm[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; model->calculateNormals(); model->getNormal( t, 0, norm ); Vector triNorm( norm[0], norm[1], norm[2] ); log_debug( "view normal is %f %f %f\n", (float) viewNorm[0], (float) viewNorm[1], (float) viewNorm[2] ); log_debug( "triangle normal is %f %f %f\n", (float) triNorm[0], (float) triNorm[1], (float) triNorm[2] ); double d = viewNorm.dot3( triNorm ); log_debug( "dot product is %f\n", (float) d ); if( d < 0 ) { model->invertNormals( t ); } } else { model->beginSelectionDifference(); model->selectVertex( m_lastVertex.pos.index ); } model->endSelectionDifference(); parent->updateAllViews(); } } void PolyTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( buttonState == BS_Left ) { if ( m_lastVertex.pos.type == Model::PT_Vertex ) { double coord[3] = {0,0,0}; // I chose false for the same reason as above parent->getParentXYValue( x, y, coord[0], coord[1], false ); movePosition( parent, m_lastVertex.pos, coord[0], coord[1], coord[2] ); parent->updateAllViews(); } } } void PolyTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { } const char ** PolyTool::getPixmap() { return (const char **) polytool_xpm; } void PolyTool::setTypeValue( int newValue ) { m_type = newValue; } const char * PolyTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Polygon" ); } mm3d-1.3.15/src/tools/polytool.h000066400000000000000000000035011466047437300164370ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __POLYTOOL_H #define __POLYTOOL_H #include "tool.h" #include "polytoolwidget.h" #include typedef std::list IntList; class PolyTool : public Tool, public PolyToolWidget::Observer { public: PolyTool(); virtual ~PolyTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool isCreation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // Observer methods void setTypeValue( int newValue ); protected: Model * m_model; ToolCoordT m_lastVertex; IntList m_verts; int m_type; PolyToolWidget * m_widget; }; #endif // __POLYTOOL_H mm3d-1.3.15/src/tools/polytoolwidget.cc000066400000000000000000000042431466047437300200050ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "polytoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include #include PolyToolWidget::PolyToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const int DEFAULT_FAN = 0; m_layout = boxLayout(); m_typeLabel = new QLabel( tr("Poly Type"), mainWidget() ); m_layout->addWidget( m_typeLabel ); m_typeValue = new QComboBox( mainWidget() ); m_typeValue->insertItem( 0, tr("Strip", "Triangle strip option") ); m_typeValue->insertItem( 1, tr("Fan", "Triangle fan option") ); m_layout->addWidget( m_typeValue ); g_prefs.setDefault( "ui_polytool_is_fan", DEFAULT_FAN ); int index = g_prefs( "ui_polytool_isfan" ).intValue(); m_typeValue->setCurrentIndex( (index == 0) ? 0 : 1 ); m_layout->addStretch(); connect( m_typeValue, SIGNAL(activated(int)), this, SLOT(typeValueChanged(int)) ); m_typeLabel->show(); m_typeValue->show(); typeValueChanged( index ); } PolyToolWidget::~PolyToolWidget() { } void PolyToolWidget::typeValueChanged( int newValue ) { g_prefs( "ui_polytool_isfan" ) = newValue; m_observer->setTypeValue( newValue ); } mm3d-1.3.15/src/tools/polytoolwidget.h000066400000000000000000000033301466047437300176430ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __POLYTOOLWIDGET_H #define __POLYTOOLWIDGET_H class QMainWindow; class QLabel; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QSpinBox; class QComboBox; class QLabel; #include "toolwidget.h" class PolyToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setTypeValue( int type ) = 0; }; PolyToolWidget( Observer * observer, QMainWindow * parent ); virtual ~PolyToolWidget(); public slots: void typeValueChanged( int type ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_typeLabel; QComboBox * m_typeValue; QLabel * m_segmentLabel; QSpinBox * m_segmentValue; }; #endif // __POLYTOOLWIDGET_H mm3d-1.3.15/src/tools/projtool.cc000066400000000000000000000125371466047437300165750ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "projtool.h" #include "projtoolwidget.h" #include "model.h" #include "msg.h" #include "log.h" #include "modelstatus.h" #include "pixmap/projtool.xpm" #include #include ProjectionTool::ProjectionTool() : m_type( Model::TPT_Sphere ) { m_proj.pos.type = Model::PT_Vertex; } ProjectionTool::~ProjectionTool() { } void ProjectionTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_allowX = true; m_allowY = true; m_x = x; m_y = y; Model * model = parent->getModel(); model->setDrawProjections( true ); // use parent view matrix to translate 2D coords into 3D coords double coord[4] = {0,0,0,1}; parent->getParentXYValue( x, y, coord[0], coord[1], true ); Matrix m = parent->getParentViewInverseMatrix(); m.apply( coord ); // Find a unique name for the projection char name[32] = "Projection 1"; unsigned c = model->getProjectionCount(); bool uniqueName = (c == 0) ? true : false; for ( unsigned i = 1; !uniqueName && i < 1000; i++ ) { uniqueName = true; sprintf( name, "Projection %d", i ); for ( unsigned j = 0; j < c; j++ ) { if ( strcmp( name, model->getProjectionName( j ) ) == 0 ) { uniqueName = false; break; } } } // I give up, just call it "Projection" if ( ! uniqueName ) { strcpy( name, "Projection" ); } m_orig[0] = coord[0]; m_orig[1] = coord[1]; m_orig[2] = coord[2]; // Create projection m_proj.pos.type = Model::PT_Projection; m_proj.pos.index = model->addProjection( name, static_cast( m_type ), coord[0], coord[1], coord[2] ); double upVec[3] = { 0, 1, 0 }; double seamVec[3] = { 0, 0, -1.0 / 3.0 }; m.apply3( upVec ); m.apply3( seamVec ); model->setProjectionUp( m_proj.pos.index, upVec ); model->setProjectionSeam( m_proj.pos.index, seamVec ); model->setProjectionScale( m_proj.pos.index, 1.0 ); // Assign selected faces to projection unsigned tcount = model->getTriangleCount(); for ( unsigned t = 0; t < tcount; t++ ) { if ( model->isTriangleSelected( t ) ) { model->setTriangleProjection( t, m_proj.pos.index ); } } model->applyProjection( m_proj.pos.index ); // Make new projection the only thing selected model->unselectAll(); model->selectProjection( m_proj.pos.index ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Projection created" ).toUtf8().data() ); } void ProjectionTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_proj.pos.type == Model::PT_Projection ) { if ( buttonState & BS_Shift ) { if ( m_allowX && m_allowY ) { double ax = fabs( x - m_x ); double ay = fabs( y - m_y ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { x = m_x; } if ( !m_allowY ) { y = m_y; } double coord[3] = {0,0,0}; // Convert 2D coords to 3D in parent's view space parent->getParentXYValue( x, y, coord[0], coord[1] ); Matrix m = parent->getParentViewInverseMatrix(); double tranVec[4] = { coord[0], coord[1], 0.0, 1.0 }; m.apply( tranVec ); tranVec[0] -= m_orig[0]; tranVec[1] -= m_orig[1]; tranVec[2] -= m_orig[2]; // Set the up vector to whereever the mouse is Model * model = parent->getModel(); model->setProjectionUp( m_proj.pos.index, tranVec ); //model->applyProjection( m_proj.pos.index ); parent->updateAllViews(); } } void ProjectionTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { } const char ** ProjectionTool::getPixmap() { return (const char **) projtool_xpm; } const char * ProjectionTool::getPath() { return TOOLS_CREATE_MENU; } const char * ProjectionTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Projection" ); } void ProjectionTool::setTypeValue( int newValue ) { m_type = newValue; } void ProjectionTool::activated( int arg, Model * model, QMainWindow * mainwin ) { m_widget = new ProjToolWidget( this, mainwin ); m_widget->show(); } void ProjectionTool::deactivated() { m_widget->close(); } mm3d-1.3.15/src/tools/projtool.h000066400000000000000000000035621466047437300164350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __PROJTOOL_H #define __PROJTOOL_H #include "tool.h" #include "projtoolwidget.h" class ProjectionTool : public Tool, public ProjToolWidget::Observer { public: ProjectionTool(); virtual ~ProjectionTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool isCreation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // Observer methods void setTypeValue( int newValue ); protected: ToolCoordT m_proj; double m_orig[3]; int m_x; int m_y; bool m_allowX; bool m_allowY; int m_type; ProjToolWidget * m_widget; }; #endif // __PROJTOOL_H mm3d-1.3.15/src/tools/projtoolwidget.cc000066400000000000000000000046631466047437300200020ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "projtoolwidget.h" #include "3dmprefs.h" #include "model.h" #include #include #include #include #include #include ProjToolWidget::ProjToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const int DEFAULT_TYPE = Model::TPT_Sphere; m_layout = boxLayout(); m_typeLabel = new QLabel( tr("Type"), mainWidget() ); m_layout->addWidget( m_typeLabel ); m_typeValue = new QComboBox( mainWidget() ); m_layout->addWidget( m_typeValue ); m_typeValue->insertItem( Model::TPT_Cylinder, tr("Cylinder", "Cylinder projection type") ); m_typeValue->insertItem( Model::TPT_Sphere, tr("Sphere", "Sphere projection type") ); m_typeValue->insertItem( Model::TPT_Plane, tr("Plane", "Plane projection type") ); int typeIndex = DEFAULT_TYPE; g_prefs.setDefault( "ui_projtool_type_index", DEFAULT_TYPE ); int temp = g_prefs( "ui_projtool_type_index" ).intValue(); if ( temp >= Model::TPT_Cylinder && temp <= Model::TPT_Plane ) { typeIndex = temp; } m_typeValue->setCurrentIndex( typeIndex ); m_layout->addStretch(); connect( m_typeValue, SIGNAL(activated(int)), this, SLOT(typeValueChanged(int)) ); m_typeLabel->show(); m_typeValue->show(); typeValueChanged( typeIndex ); } ProjToolWidget::~ProjToolWidget() { } void ProjToolWidget::typeValueChanged( int newValue ) { g_prefs( "ui_projtool_type_index" ) = newValue; m_observer->setTypeValue( newValue ); } mm3d-1.3.15/src/tools/projtoolwidget.h000066400000000000000000000031321466047437300176320ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __PROJTOOLWIDGET_H #define __PROJTOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QComboBox; class QLabel; #include "toolwidget.h" class ProjToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setTypeValue( int newValue ) = 0; }; ProjToolWidget( Observer * observer, QMainWindow * parent ); virtual ~ProjToolWidget(); public slots: void typeValueChanged( int newValue ); protected: Observer * m_observer; QBoxLayout * m_layout; QLabel * m_typeLabel; QComboBox * m_typeValue; }; #endif // __PROJTOOLWIDGET_H mm3d-1.3.15/src/tools/rectangletool.cc000066400000000000000000000066471466047437300175740ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "rectangletool.h" #include "pixmap/rectangletool.xpm" #include "log.h" #include "model.h" #include "modelstatus.h" #include #include RectangleTool::RectangleTool() : m_tracking( false ), m_parent( NULL ) { } RectangleTool::~RectangleTool() { } void RectangleTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( !m_tracking ) { m_parent = parent; // Keep track of which parent we're serving m_tracking = true; m_x1 = 0.0; m_y1 = 0.0; parent->getParentXYValue( x, y, m_x1, m_y1 ); Model * model = parent->getModel(); model->unselectAll(); log_debug( "model has %d vertices\n", model->getVertexCount() ); m_v1 = addPosition( parent, Model::PT_Vertex, NULL, m_x1, m_y1, 0.0 ); m_v2 = addPosition( parent, Model::PT_Vertex, NULL, m_x1, m_y1, 0.0 ); m_v3 = addPosition( parent, Model::PT_Vertex, NULL, m_x1, m_y1, 0.0 ); m_v4 = addPosition( parent, Model::PT_Vertex, NULL, m_x1, m_y1, 0.0 ); log_debug( "last new vertex: %d\n", m_v4.pos.index ); model->addTriangle( m_v1.pos.index, m_v2.pos.index, m_v4.pos.index ); model->addTriangle( m_v4.pos.index, m_v3.pos.index, m_v1.pos.index ); model->selectVertex( m_v1.pos.index ); model->selectVertex( m_v2.pos.index ); model->selectVertex( m_v3.pos.index ); model->selectVertex( m_v4.pos.index ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Rectangle created" ).toUtf8().data() ); } } void RectangleTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { m_tracking = false; } void RectangleTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( parent != m_parent ) { log_error( "Can't serve two parents at once\n" ); } if ( m_tracking ) { double x2 = 0.0; double y2 = 0.0; parent->getParentXYValue( x, y, x2, y2 ); movePosition( parent, m_v2.pos, m_x1, y2, 0.0 ); movePosition( parent, m_v3.pos, x2, m_y1, 0.0 ); movePosition( parent, m_v4.pos, x2, y2, 0.0 ); parent->updateAllViews(); } } const char ** RectangleTool::getPixmap() { return (const char **) rectangletool_xpm; } const char * RectangleTool::getPath() { return TOOLS_CREATE_MENU; } const char * RectangleTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Rectangle" ); } mm3d-1.3.15/src/tools/rectangletool.h000066400000000000000000000032251466047437300174230ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __RECTANGLETOOL_H #define __RECTANGLETOOL_H #include "tool.h" class RectangleTool : public Tool { public: RectangleTool(); virtual ~RectangleTool(); int getToolCount() { return 1; }; const char * getPath(); const char * getName( int arg ); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: bool m_tracking; Parent * m_parent; ToolCoordT m_v1; ToolCoordT m_v2; ToolCoordT m_v3; ToolCoordT m_v4; double m_x1; double m_y1; }; #endif // __RECTANGLETOOL_H mm3d-1.3.15/src/tools/rotatetool.cc000066400000000000000000000224661466047437300171230ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "rotatetool.h" #include "model.h" #include "pixmap/rotatetool.xpm" #include "glmath.h" #include "decalmgr.h" #include "rotatepoint.h" #include "log.h" #include "modelstatus.h" #include #include #include static void _add_coords( double * dest, double * rhs, double * lhs ) { dest[0] = rhs[0] + lhs[0]; dest[1] = rhs[1] + lhs[1]; dest[2] = rhs[2] + lhs[2]; } RotateTool::RotateTool() : m_model( NULL ), m_tracking( false ), m_widget( NULL ) { } RotateTool::~RotateTool() { } void RotateTool::setModel( Model * model ) { m_model = model; } void RotateTool::activated( int arg, Model * model, QMainWindow * mainwin ) { m_model = model; model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Tip: Hold shift to rotate in 15 degree increments" ).toUtf8().data() ); m_coords[0] = 0; m_coords[1] = 0; m_coords[2] = 0; m_coords[3] = 1; if ( model->getAnimationMode() == Model::ANIMMODE_SKELETAL ) { list joints; model->getSelectedBoneJoints( joints ); if ( joints.size() > 0 ) { model->getBoneJointCoords( joints.front(), m_coords ); } } else { double coords[3]; unsigned count = 0; unsigned vcount = model->getVertexCount(); for ( unsigned int v = 0; v < vcount; v++ ) { if ( model->isVertexSelected( v ) ) { model->getVertexCoords( v, coords ); _add_coords( m_coords, m_coords, coords ); count++; } } unsigned int bcount = model->getBoneJointCount(); for ( unsigned int b = 0; b < bcount; b++ ) { if ( model->isBoneJointSelected( b ) ) { model->getBoneJointCoords( b, coords ); _add_coords( m_coords, m_coords, coords ); count++; } } unsigned int pcount = model->getPointCount(); for ( unsigned int p = 0; p < pcount; p++ ) { if ( model->isPointSelected( p ) ) { model->getPointTranslation( p, coords ); _add_coords( m_coords, m_coords, coords ); count++; } } unsigned int rcount = model->getProjectionCount(); for ( unsigned int r = 0; r < rcount; r++ ) { if ( model->isProjectionSelected( r ) ) { model->getProjectionCoords( r, coords ); _add_coords( m_coords, m_coords, coords ); count++; } } m_coords[0] = m_coords[0] / (double) count; m_coords[1] = m_coords[1] / (double) count; m_coords[2] = m_coords[2] / (double) count; } m_rotatePoint = new RotatePoint(); m_rotatePoint->setPoint( m_coords[0], m_coords[1], m_coords[2] ); DecalManager::getInstance()->addDecalToModel( m_rotatePoint, model ); m_widget = new RotateToolWidget( this, mainwin, m_coords[0], m_coords[1], m_coords[2] ); m_widget->show(); } void RotateTool::deactivated() { DecalManager::getInstance()->removeDecal( m_rotatePoint ); delete m_rotatePoint; m_rotatePoint = NULL; m_widget->close(); m_widget = NULL; } void RotateTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); m_model = model; if ( model->getAnimationMode() == Model::ANIMMODE_SKELETAL ) { list joints; model->getSelectedBoneJoints( joints ); if ( joints.size() > 0 ) { model->getBoneJointCoords( joints.front(), m_coords ); m_widget->setCoords( m_coords[0], m_coords[1], m_coords[2] ); m_rotatePoint->setPoint( m_coords[0], m_coords[1], m_coords[2] ); } } double newCoords[3] = {0,0,0}; parent->getParentXYValue( x, y, newCoords[0], newCoords[1], true ); m_viewMatrix = parent->getParentViewMatrix(); m_viewInverse = parent->getParentViewInverseMatrix(); if ( buttonState & BS_Left ) { m_tracking = true; getRotateCoords( parent ); double xDiff = newCoords[0] - m_coords[0]; double yDiff = newCoords[1] - m_coords[1]; double angle = diffToAngle( yDiff, xDiff ); if ( buttonState & BS_Shift ) { angle = adjustToNearest( angle ); } m_startAngle = angle; model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Rotating selected primitives" ).toUtf8().data() ); } else if ( buttonState & BS_Right && model->getAnimationMode() != Model::ANIMMODE_SKELETAL ) { for ( int t = 0; t < 3; t++ ) { m_coords[t] = newCoords[t]; } m_coords[3] = 1; m_viewInverse.apply( m_coords ); m_widget->setCoords( m_coords[0], m_coords[1], m_coords[2] ); m_rotatePoint->setPoint( m_coords[0], m_coords[1], m_coords[2] ); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Setting rotation point" ).toUtf8().data() ); } parent->updateAllViews(); } void RotateTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); m_model = model; double newCoords[3] = {0,0,0}; parent->getParentXYValue( x, y, newCoords[0], newCoords[1] ); if ( buttonState & BS_Left ) { getRotateCoords( parent ); double xDiff = newCoords[0] - m_coords[0]; double yDiff = newCoords[1] - m_coords[1]; double angles[3] = { 0, 0, 0 }; Matrix m; double angle = diffToAngle( yDiff, xDiff ); if ( buttonState & BS_Shift ) { angle = adjustToNearest( angle ); } angles[2] = (angle - m_startAngle); double vec[4] = { 0, 0, 1, 1 }; m_viewInverse.apply3( vec ); m.setRotationOnAxis( vec, angle - m_startAngle ); m_coords[3] = 1; m_viewInverse.apply( m_coords ); model->rotateSelected( m, m_coords ); m_startAngle = angle; } else if ( buttonState & BS_Right ) { getRotateCoords( parent ); for ( int t = 0; t < 3; t++ ) { m_coords[t] = newCoords[t]; } m_coords[3] = 1; m_viewInverse.apply( m_coords ); m_widget->setCoords( m_coords[0], m_coords[1], m_coords[2] ); m_rotatePoint->setPoint( m_coords[0], m_coords[1], m_coords[2] ); } parent->updateAllViews(); } void RotateTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { parent->updateAllViews(); m_tracking = false; if ( buttonState & BS_Left ) { model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Rotate complete" ).toUtf8().data() ); } } const char ** RotateTool::getPixmap() { return (const char **) rotatetool_xpm; } double RotateTool::diffToAngle( double opposite, double adjacent ) { if ( adjacent < 0.0001 && adjacent > -0.0001 ) { adjacent = (adjacent >= 0 ) ? 0.0001 : -0.0001; } double angle = atan( opposite / adjacent ); float quad = PIOVER180 * 90; if ( adjacent < 0 ) { if ( opposite < 0 ) { angle = -(quad) - ( (quad) - angle ); } else { angle = (quad) + ( (quad) + angle ); } } return angle; } void RotateTool::getRotateCoords( Tool::Parent * parent ) { m_rotatePoint->getPoint( m_coords[0], m_coords[1], m_coords[2] ); m_coords[3] = 1; m_viewMatrix.apply( m_coords ); } double RotateTool::adjustToNearest( double angle ) { double f = angle / PIOVER180; // Change to degrees if ( f < 0.0 ) { int n = (int) (f / 15.0 - 0.5); f = n * 15.0; } else { int n = (int) (f / 15.0 + 0.5); f = n * 15.0; } log_debug( "nearest angle is %f\n", f ); return f * PIOVER180; } const char * RotateTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Rotate" ); } void RotateTool::setXValue( double newValue ) { double x = 0; double y = 0; double z = 0; m_rotatePoint->getPoint( x, y, z ); x = newValue; m_rotatePoint->setPoint( x, y, z ); DecalManager::getInstance()->modelUpdated( m_model ); } void RotateTool::setYValue( double newValue ) { double x = 0; double y = 0; double z = 0; m_rotatePoint->getPoint( x, y, z ); y = newValue; m_rotatePoint->setPoint( x, y, z ); DecalManager::getInstance()->modelUpdated( m_model ); } void RotateTool::setZValue( double newValue ) { double x = 0; double y = 0; double z = 0; m_rotatePoint->getPoint( x, y, z ); z = newValue; m_rotatePoint->setPoint( x, y, z ); DecalManager::getInstance()->modelUpdated( m_model ); } mm3d-1.3.15/src/tools/rotatetool.h000066400000000000000000000046141466047437300167600ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __ROTATETOOL_H #define __ROTATETOOL_H #include "tool.h" #include #include #include "rotatetoolwidget.h" class Model; class RotatePoint; class RotateTool : public Tool, public RotateToolWidget::Observer { public: RotateTool(); virtual ~RotateTool(); int getToolCount() { return 1; }; const char * getName( int arg ); void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void setModel( Model * model ); bool getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_R; return true; }; bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // RotateToolWidget::Observer void setXValue( double newValue ); void setYValue( double newValue ); void setZValue( double newValue ); protected: Model * m_model; Matrix m_viewMatrix; Matrix m_viewInverse; double diffToAngle( double opposite, double adjacent ); double adjustToNearest( double angle ); void getRotateCoords( Tool::Parent * ); double m_startAngle; double m_originX; double m_originY; double m_coords[4]; bool m_tracking; RotatePoint * m_rotatePoint; RotateToolWidget * m_widget; }; #endif // __ROTATETOOL_H mm3d-1.3.15/src/tools/rotatetoolwidget.cc000066400000000000000000000066461466047437300203310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "rotatetoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include RotateToolWidget::RotateToolWidget( Observer * observer, QMainWindow * parent, double x, double y, double z ) : ToolWidget ( parent ), m_observer( observer ), m_ignore( false ) { m_layout = boxLayout(); m_xLabel = new QLabel( tr("X"), mainWidget() ); m_layout->addWidget( m_xLabel ); m_xValue = new QLineEdit( mainWidget() ); m_xValue->setMinimumWidth( 100 ); m_layout->addWidget( m_xValue ); m_xValue->setText( QString::number( x, 'f' ) ); m_yLabel = new QLabel( tr("Y"), mainWidget() ); m_layout->addWidget( m_yLabel ); m_yValue = new QLineEdit( mainWidget() ); m_yValue->setMinimumWidth( 100 ); m_layout->addWidget( m_yValue ); m_yValue->setText( QString::number( y, 'f' ) ); m_zLabel = new QLabel( tr("Z"), mainWidget() ); m_layout->addWidget( m_zLabel ); m_zValue = new QLineEdit( mainWidget() ); m_zValue->setMinimumWidth( 100 ); m_layout->addWidget( m_zValue ); m_zValue->setText( QString::number( z, 'f' ) ); m_layout->addStretch(); connect( m_xValue, SIGNAL(textChanged(const QString &)), this, SLOT(xValueChanged(const QString &)) ); connect( m_yValue, SIGNAL(textChanged(const QString &)), this, SLOT(yValueChanged(const QString &)) ); connect( m_zValue, SIGNAL(textChanged(const QString &)), this, SLOT(zValueChanged(const QString &)) ); m_xLabel->show(); m_xValue->show(); m_yLabel->show(); m_yValue->show(); m_zLabel->show(); m_zValue->show(); // Unlike other tool widgets, our initial settings came from the tool, // so we don't call the slots directly to update the tool's settings. } RotateToolWidget::~RotateToolWidget() { } void RotateToolWidget::xValueChanged( const QString & newValue ) { if ( m_ignore ) return; m_observer->setXValue( newValue.toDouble() ); } void RotateToolWidget::yValueChanged( const QString & newValue ) { if ( m_ignore ) return; m_observer->setYValue( newValue.toDouble() ); } void RotateToolWidget::zValueChanged( const QString & newValue ) { if ( m_ignore ) return; m_observer->setZValue( newValue.toDouble() ); } void RotateToolWidget::setCoords( double x, double y, double z ) { m_ignore = true; m_xValue->setText( QString::number( x, 'f' ) ); m_yValue->setText( QString::number( y, 'f' ) ); m_zValue->setText( QString::number( z, 'f' ) ); m_ignore = false; } mm3d-1.3.15/src/tools/rotatetoolwidget.h000066400000000000000000000041571466047437300201660ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef ROTATETOOLWIDGET_H_INC__ #define ROTATETOOLWIDGET_H_INC__ class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QLineEdit; class QLabel; #include "toolwidget.h" class RotateToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setXValue( double newValue ) = 0; virtual void setYValue( double newValue ) = 0; virtual void setZValue( double newValue ) = 0; }; RotateToolWidget( Observer * observer, QMainWindow * parent, double x, double y, double z ); virtual ~RotateToolWidget(); void setCoords( double x, double y, double z ); public slots: void xValueChanged( const QString & newValue ); void yValueChanged( const QString & newValue ); void zValueChanged( const QString & newValue ); protected: Observer * m_observer; bool m_ignore; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_xLabel; QLineEdit * m_xValue; QLabel * m_yLabel; QLineEdit * m_yValue; QLabel * m_zLabel; QLineEdit * m_zValue; }; #endif // ROTATETOOLWIDGET_H_INC__ mm3d-1.3.15/src/tools/scaletool.cc000066400000000000000000000224131466047437300167040ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "scaletool.h" #include "model.h" #include "log.h" #include "modelstatus.h" #include "pixmap/scaletool.xpm" #include #include #include ScaleTool::ScaleTool() { } ScaleTool::~ScaleTool() { } const char * ScaleTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Scale" ); } bool ScaleTool::getKeyBinding( int arg, int & keyBinding ) { return false; } void ScaleTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_positionCoords.clear(); m_allowX = true; m_allowY = true; Model * model = parent->getModel(); list posList; model->getSelectedPositions( posList ); m_positionCoords.clear(); makeToolCoordList( parent, m_positionCoords, posList ); ToolCoordList::iterator it; bool firstSet = false; for ( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { // update range if ( !firstSet ) { m_minX = (*it).oldCoords[0]; m_minY = (*it).oldCoords[1]; m_minZ = (*it).oldCoords[2]; m_maxX = (*it).oldCoords[0]; m_maxY = (*it).oldCoords[1]; m_maxZ = (*it).oldCoords[2]; firstSet = true; } else { if ( (*it).oldCoords[0] < m_minX ) { m_minX = (*it).oldCoords[0]; } if ( (*it).oldCoords[0] > m_maxX ) { m_maxX = (*it).oldCoords[0]; } if ( (*it).oldCoords[1] < m_minY ) { m_minY = (*it).oldCoords[1]; } if ( (*it).oldCoords[1] > m_maxY ) { m_maxY = (*it).oldCoords[1]; } if ( (*it).oldCoords[2] < m_minZ ) { m_minZ = (*it).oldCoords[2]; } if ( (*it).oldCoords[2] > m_maxZ ) { m_maxZ = (*it).oldCoords[2]; } } } double curX = 0; double curY = 0; m_startLengthX = 0; m_startLengthY = 0; parent->getParentXYValue( x, y, curX, curY, true ); m_x = curX; m_y = curY; if ( m_point == ST_ScalePointFar ) { double minmin = 0; double minmax = 0; double maxmin = 0; double maxmax = 0; m_farZ = m_minZ; minmin = distance( m_minX, m_minY, curX, curY ); minmax = distance( m_minX, m_maxY, curX, curY ); maxmin = distance( m_maxX, m_minY, curX, curY ); maxmax = distance( m_maxX, m_maxY, curX, curY ); if ( minmin > minmax ) { if ( minmin > maxmin ) { if ( minmin > maxmax ) { m_farX = m_minX; m_farY = m_minY; } else { m_farX = m_maxX; m_farY = m_maxY; } } else { // maxmin > minmin if ( maxmin > maxmax ) { m_farX = m_maxX; m_farY = m_minY; } else { m_farX = m_maxX; m_farY = m_maxY; } } } else { // minmax > minmin if ( minmax > maxmin ) { if ( minmax > maxmax ) { m_farX = m_minX; m_farY = m_maxY; } else { m_farX = m_maxX; m_farY = m_maxY; } } else { // maxmin > minmax if ( maxmin > maxmax ) { m_farX = m_maxX; m_farY = m_minY; } else { m_farX = m_maxX; m_farY = m_maxY; } } } m_startLengthX = fabs( m_farX - curX ); m_startLengthY = fabs( m_farY - curY ); } else { m_centerX = (m_maxX - m_minX) / 2.0 + m_minX; m_centerY = (m_maxY - m_minY) / 2.0 + m_minY; m_centerZ = (m_maxZ - m_minZ) / 2.0 + m_minZ; m_startLengthX = fabs( m_centerX - curX ); m_startLengthY = fabs( m_centerY - curY ); } m_projList.clear(); // remove projections (special case) for ( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { log_debug( "checking for projection\n" ); if ( (*it).pos.type == Model::PT_Projection ) { log_debug( "found projection %d\n", (*it).pos.index ); m_projList.push_back( (*it).pos.index ); } } while ( !m_positionCoords.empty() && m_positionCoords.back().pos.type == Model::PT_Projection ) { log_debug( "removing projection\n" ); m_positionCoords.pop_back(); } if ( !m_projList.empty() ) { log_debug( "getting scale\n" ); m_projScale = model->getProjectionScale( m_projList.front() ); } model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Scaling selected primitives" ).toUtf8().data() ); } void ScaleTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { LOG_PROFILE(); double curX = m_x; double curY = m_y; parent->getParentXYValue( x, y, curX, curY ); if ( buttonState & BS_Shift ) { if ( m_allowX && m_allowY ) { double ax = fabs( curX - m_x ); double ay = fabs( curY - m_y ); if ( ax > ay ) { m_allowY = false; } if ( ay > ax ) { m_allowX = false; } } } if ( !m_allowX ) { curX = m_x; } if ( !m_allowY ) { curY = m_y; } double spX = ( m_point == ST_ScalePointFar ) ? m_farX : m_centerX; double spY = ( m_point == ST_ScalePointFar ) ? m_farY : m_centerY; double spZ = ( m_point == ST_ScalePointFar ) ? m_farZ : m_centerZ; double lengthX = distance( spX, 0, curX, 0 ); double lengthY = distance( spY, 0, curY, 0 ); ToolCoordList::iterator it; for( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { double x = (*it).oldCoords[0]; double y = (*it).oldCoords[1]; double z = (*it).oldCoords[2]; x -= spX; y -= spY; z -= spZ; double xper = (lengthX / m_startLengthX); if ( m_startLengthX <= 0.00006 ) { xper = 1.0; } double yper = (lengthY / m_startLengthY); if ( m_startLengthY <= 0.00006 ) { yper = 1.0; } if ( m_proportion == ST_ScaleFree ) { x *= xper; y *= yper; } else { if ( xper > yper ) { x *= xper; y *= xper; if ( m_proportion == ST_ScaleProportion3D ) { z *= xper; } } else { x *= yper; y *= yper; if ( m_proportion == ST_ScaleProportion3D ) { z *= yper; } } } x += spX; y += spY; z += spZ; movePosition( parent, (*it).pos, x, y, z ); } if ( !m_projList.empty() ) { log_debug( "setting scale\n" ); double startLen = sqrt( m_startLengthX * m_startLengthX + m_startLengthY * m_startLengthY); double len = sqrt( lengthX * lengthX + lengthY * lengthY); double diff = len / startLen; parent->getModel()->setProjectionScale( m_projList.front(), m_projScale * diff ); log_debug( "new scale = %f\n", (float) m_projScale * diff ); } parent->updateAllViews(); } void ScaleTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( !m_projList.empty() ) { //parent->getModel()->applyProjection( m_projList.front() ); //parent->updateAllViews(); } model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Scale complete" ).toUtf8().data() ); } const char ** ScaleTool::getPixmap() { return (const char **) scaletool_xpm; } double ScaleTool::distance( double x1, double y1, double x2, double y2 ) { double xDiff = x2 - x1; double yDiff = y2 - y1; return sqrt( xDiff*xDiff + yDiff*yDiff ); } double ScaleTool::max( double a, double b ) { return ( a > b ) ? a : b; } void ScaleTool::setProportionValue( int newValue ) { m_proportion = newValue; } void ScaleTool::setPointValue( int newValue ) { m_point = newValue; } void ScaleTool::activated( int arg, Model * model, QMainWindow * mainwin ) { model_status( model, StatusNormal, STATUSTIME_NONE, "%s", qApp->translate( "Tool", "Tip: Hold shift to restrict scaling to one dimension" ).toUtf8().data() ); m_widget = new ScaleToolWidget( this, mainwin ); m_widget->show(); } void ScaleTool::deactivated() { m_widget->close(); } mm3d-1.3.15/src/tools/scaletool.h000066400000000000000000000050511466047437300165450ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SCALETOOL_H #define __SCALETOOL_H #include "tool.h" #include "model.h" #include "scaletoolwidget.h" #include using std::list; class ScaleTool : public Tool, public ScaleToolWidget::Observer { public: ScaleTool(); virtual ~ScaleTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); double distance( double x1, double y1, double x2, double y2 ); double min( double a, double b ); double max( double a, double b ); // Observer methods void setProportionValue( int newValue ); void setPointValue( int newValue ); protected: double m_x; double m_y; double m_minX; double m_maxX; double m_minY; double m_maxY; double m_minZ; double m_maxZ; double m_farX; double m_farY; double m_farZ; double m_centerX; double m_centerY; double m_centerZ; double m_startLengthX; double m_startLengthY; double m_startLengthZ; bool m_allowX; bool m_allowY; double m_projScale; std::list m_projList; ToolCoordList m_positionCoords; int m_proportion; int m_point; ScaleToolWidget * m_widget; }; #endif // __SCALETOOL_H mm3d-1.3.15/src/tools/scaletoolwidget.cc000066400000000000000000000071171466047437300201140ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "scaletoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include ScaleToolWidget::ScaleToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const int DEFAULT_PROPORTION = ST_ScaleFree; const int DEFAULT_POINT = ST_ScalePointCenter; m_layout = boxLayout(); m_proportionLabel = new QLabel( tr("Proportion"), mainWidget() ); m_layout->addWidget( m_proportionLabel ); m_proportionValue = new QComboBox( mainWidget() ); m_layout->addWidget( m_proportionValue ); m_proportionValue->insertItem( ST_ScaleFree, tr("Free", "Free scaling option") ); m_proportionValue->insertItem( ST_ScaleProportion2D, tr("Keep Aspect 2D", "2D scaling aspect option") ); m_proportionValue->insertItem( ST_ScaleProportion3D, tr("Keep Aspect 3D", "3D scaling aspect option") ); int aspectIndex = DEFAULT_PROPORTION; if ( g_prefs.exists("ui_scaletool_aspect_index") ) { int temp = g_prefs( "ui_scaletool_aspect_index" ).intValue(); if ( temp >= 0 && temp < 3 ) { aspectIndex = temp; } } m_proportionValue->setCurrentIndex( aspectIndex ); connect( m_proportionValue, SIGNAL(activated(int)), this, SLOT(proportionValueChanged(int)) ); m_pointLabel = new QLabel( tr("Point"), mainWidget() ); m_layout->addWidget( m_pointLabel ); m_pointValue = new QComboBox( mainWidget() ); m_layout->addWidget( m_pointValue ); m_pointValue->insertItem( ST_ScalePointCenter, tr("Center", "Scale from center") ); m_pointValue->insertItem( ST_ScalePointFar, tr("Far Corner", "Scale from far corner") ); int pointIndex = DEFAULT_POINT; if ( g_prefs.exists("ui_scaletool_point_index") ) { int temp = g_prefs( "ui_scaletool_point_index" ).intValue(); if ( temp >= ST_ScalePointCenter && temp <= ST_ScalePointFar ) { pointIndex = temp; } } m_pointValue->setCurrentIndex( pointIndex ); m_layout->addStretch(); connect( m_pointValue, SIGNAL(activated(int)), this, SLOT(pointValueChanged(int)) ); m_proportionLabel->show(); m_proportionValue->show(); m_pointLabel->show(); m_pointValue->show(); proportionValueChanged( aspectIndex ); pointValueChanged( pointIndex ); } ScaleToolWidget::~ScaleToolWidget() { } void ScaleToolWidget::proportionValueChanged( int newValue ) { g_prefs( "ui_scaletool_aspect_index" ) = newValue; m_observer->setProportionValue( newValue ); } void ScaleToolWidget::pointValueChanged( int newValue ) { g_prefs( "ui_scaletool_point_index" ) = newValue; m_observer->setPointValue( newValue ); } mm3d-1.3.15/src/tools/scaletoolwidget.h000066400000000000000000000041471466047437300177560ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SCALETOOLWIDGET_H #define __SCALETOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QComboBox; class QLabel; enum ScaleProportion_e { ST_ScaleFree = 0, ST_ScaleProportion2D = 1, ST_ScaleProportion3D = 2, }; typedef enum ScaleProportion_e ScaleProportionE; enum _ScalePoint_e { ST_ScalePointCenter = 0, ST_ScalePointFar = 1, }; typedef enum _ScalePoint_e ScalePointE; #include "toolwidget.h" class ScaleToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setProportionValue( int newValue ) = 0; virtual void setPointValue( int newValue ) = 0; }; ScaleToolWidget( Observer * observer, QMainWindow * parent ); virtual ~ScaleToolWidget(); public slots: void proportionValueChanged( int newValue ); void pointValueChanged( int newValue ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_proportionLabel; QComboBox * m_proportionValue; QLabel * m_pointLabel; QComboBox * m_pointValue; }; #endif // __SCALETOOLWIDGET_H mm3d-1.3.15/src/tools/selectbonetool.cc000066400000000000000000000105311466047437300177360ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "menuconf.h" #include "config.h" #include "selectbonetool.h" #include "3dmprefs.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectbonetool.xpm" #include #include #include #include SelectBoneTool::SelectBoneTool() : m_boundingBox( NULL), m_tracking( false ), m_unselect( false ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectJoints ) { } SelectBoneTool::~SelectBoneTool() { } void SelectBoneTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectJoints ); Model::DrawJointModeE mode = static_cast( g_prefs( "ui_draw_joints" ).intValue() ); if ( mode == Model::JOINTMODE_NONE ) mode = Model::JOINTMODE_BONES; parent->getModel()->setDrawJoints( mode ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectBoneTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectBoneTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectBoneTool::getPixmap() { return (const char **) selectbonetool_xpm; } const char * SelectBoneTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectBoneTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Bone Joints" ); } bool SelectBoneTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_B; return true; } mm3d-1.3.15/src/tools/selectbonetool.h000066400000000000000000000034561466047437300176100ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTBONETOOL_H #define __SELECTBONETOOL_H #include "tool.h" #include "model.h" class BoundingBox; class SelectBoneTool : public Tool { public: SelectBoneTool(); virtual ~SelectBoneTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; int m_startX; int m_startY; double m_x1; double m_y1; Matrix m_mat; Model::SelectionModeE m_selectionMode; }; #endif // __SELECTBONETOOL_H mm3d-1.3.15/src/tools/selectconnectedtool.cc000066400000000000000000000102251466047437300207550ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "menuconf.h" #include "selectconnectedtool.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectconnectedtool.xpm" #include #include #include #include SelectConnectedTool::SelectConnectedTool() : m_boundingBox( NULL), m_tracking( false ), m_unselect( false ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectConnected ) { } SelectConnectedTool::~SelectConnectedTool() { } void SelectConnectedTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectConnected ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectConnectedTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectConnectedTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectConnectedTool::getPixmap() { return (const char **) selectconnectedtool_xpm; } const char * SelectConnectedTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectConnectedTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Connected Mesh" ); } bool SelectConnectedTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_C; return true; } mm3d-1.3.15/src/tools/selectconnectedtool.h000066400000000000000000000035141466047437300206220ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTCONNECTEDTOOL_H #define __SELECTCONNECTEDTOOL_H #include "tool.h" #include "model.h" class BoundingBox; class SelectConnectedTool : public Tool { public: SelectConnectedTool(); virtual ~SelectConnectedTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; int m_startX; int m_startY; double m_x1; double m_y1; Matrix m_mat; Model::SelectionModeE m_selectionMode; }; #endif // __SELECTCONNECTEDTOOL_H mm3d-1.3.15/src/tools/selectfacetool.cc000066400000000000000000000144221466047437300177140ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "menuconf.h" #include "selectfacetool.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectfacetool.xpm" #include #include #include #include class NormalTest : public Model::SelectionTest { public: NormalTest( Model * model, const Matrix & mat ) { m_model = model; m_mat = mat; } // These should go into one of the Vector classes static double dot( const double v1[3], const double v2[3] ) { return (v1[0] * v2[0]) + (v1[1] * v2[1]) + (v1[2] * v2[2]); } static double * cross( const double v1[3], const double v2[3], double result[3]) { result[0] = v1[1] * v2[2] - v1[2] * v2[1]; result[1] = v1[0] * v2[2] - v1[2] * v2[0]; result[2] = v1[0] * v2[1] - v1[1] * v2[0]; return result; } bool shouldSelect( void * element ) { Model::Triangle * tri = static_cast( element ); if (tri) { double v0[3]; m_model->getVertexCoords( tri->m_vertexIndices[0], v0 ); m_mat.apply3( v0 ); double v1[3]; m_model->getVertexCoords( tri->m_vertexIndices[1], v1 ); m_mat.apply3( v1 ); double v2[3]; m_model->getVertexCoords( tri->m_vertexIndices[2], v2 ); m_mat.apply3( v2 ); v1[0] -= v0[0]; v1[1] -= v0[1]; v1[2] -= v0[2]; v2[0] -= v0[0]; v2[1] -= v0[1]; v2[2] -= v0[2]; double normal[3]; cross( v1, v2, normal ); double vec[3] = { 0.0, 0.0, 1.0 }; return ( dot( vec, normal ) > 0.0 ); } else { return false; } } private: Model * m_model; Matrix m_mat; }; SelectFaceTool::SelectFaceTool() : m_boundingBox( NULL), m_tracking( false ), m_unselect( false ), m_includeBackfacing( true ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectTriangles ), m_widget( NULL ) { } SelectFaceTool::~SelectFaceTool() { } void SelectFaceTool::activated( int arg, Model * model, QMainWindow * mainwin ) { m_widget = new SelectFaceToolWidget( this, mainwin ); m_widget->show(); } void SelectFaceTool::deactivated() { m_widget->close(); } void SelectFaceTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectTriangles ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectFaceTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); Model::SelectionTest * test = NULL; if ( !m_includeBackfacing ) { test = new NormalTest( model, m_mat ); } parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2, test ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2, test ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectFaceTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectFaceTool::getPixmap() { return (const char **) selectfacetool_xpm; } const char * SelectFaceTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectFaceTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Faces" ); } bool SelectFaceTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_F; return true; } void SelectFaceTool::setBackfacingValue( bool newValue ) { m_includeBackfacing = newValue; log_debug( "includeBackfacing = %s\n", newValue ? "true" : "false" ); } mm3d-1.3.15/src/tools/selectfacetool.h000066400000000000000000000041621466047437300175560ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTFACETOOL_H #define __SELECTFACETOOL_H #include "tool.h" #include "model.h" #include "selectfacetoolwidget.h" class BoundingBox; class SelectFaceTool : public Tool, public SelectFaceToolWidget::Observer { public: SelectFaceTool(); virtual ~SelectFaceTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // Observer methods void setBackfacingValue( bool newValue ); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; bool m_includeBackfacing; int m_startX; int m_startY; double m_x1; double m_y1; Model::SelectionModeE m_selectionMode; Matrix m_mat; SelectFaceToolWidget * m_widget; }; #endif // __SELECTFACETOOL_H mm3d-1.3.15/src/tools/selectfacetoolwidget.cc000066400000000000000000000043711466047437300211220ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "selectfacetoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include SelectFaceToolWidget::SelectFaceToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const bool DEFAULT_BACKFACING = true; m_layout = boxLayout(); m_backfacingLabel = new QLabel( tr("Include Back-facing"), mainWidget() ); m_layout->addWidget( m_backfacingLabel ); m_backfacingValue = new QCheckBox( mainWidget() ); m_layout->addWidget( m_backfacingValue ); bool includeBackfacing = DEFAULT_BACKFACING; if ( g_prefs.exists( "ui_selectfacetool_backfacing" ) ) { includeBackfacing = (g_prefs( "ui_selectfacetool_backfacing" ).intValue() != 0) ? true : false; } m_backfacingValue->setChecked( includeBackfacing ); m_layout->addStretch(); connect( m_backfacingValue, SIGNAL(toggled(bool)), this, SLOT(backfacingValueChanged(bool)) ); m_backfacingLabel->show(); m_backfacingValue->show(); backfacingValueChanged( includeBackfacing ); } SelectFaceToolWidget::~SelectFaceToolWidget() { } void SelectFaceToolWidget::backfacingValueChanged( bool newValue ) { g_prefs( "ui_selectfacetool_backfacing" ) = newValue ? 1 : 0; m_observer->setBackfacingValue( newValue ); } mm3d-1.3.15/src/tools/selectfacetoolwidget.h000066400000000000000000000033131466047437300207570ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTFACETOOLWIDGET_H #define __SELECTFACETOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QSpinBox; class QCheckBox; class QLabel; #include "toolwidget.h" class SelectFaceToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setBackfacingValue( bool newValue ) = 0; }; SelectFaceToolWidget( Observer * observer, QMainWindow * parent ); virtual ~SelectFaceToolWidget(); public slots: void backfacingValueChanged( bool backfacing ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_backfacingLabel; QCheckBox * m_backfacingValue; }; #endif // __SELECTFACETOOLWIDGET_H mm3d-1.3.15/src/tools/selectgrouptool.cc000066400000000000000000000101171466047437300201470ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "menuconf.h" #include "selectgrouptool.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectgrouptool.xpm" #include #include #include #include SelectGroupTool::SelectGroupTool() : m_boundingBox( NULL), m_tracking( false ), m_unselect( false ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectGroups ) { } SelectGroupTool::~SelectGroupTool() { } void SelectGroupTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectGroups ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectGroupTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectGroupTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectGroupTool::getPixmap() { return (const char **) selectgrouptool_xpm; } const char * SelectGroupTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectGroupTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Groups" ); } bool SelectGroupTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_G; return true; } mm3d-1.3.15/src/tools/selectgrouptool.h000066400000000000000000000034641466047437300200200ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTGROUPTOOL_H #define __SELECTGROUPTOOL_H #include "tool.h" #include "model.h" class BoundingBox; class SelectGroupTool : public Tool { public: SelectGroupTool(); virtual ~SelectGroupTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; int m_startX; int m_startY; double m_x1; double m_y1; Matrix m_mat; Model::SelectionModeE m_selectionMode; }; #endif // __SELECTGROUPTOOL_H mm3d-1.3.15/src/tools/selectpointtool.cc000066400000000000000000000101171466047437300201440ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "menuconf.h" #include "selectpointtool.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectpointtool.xpm" #include #include #include #include SelectPointTool::SelectPointTool() : m_boundingBox( NULL), m_tracking( false ), m_unselect( false ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectPoints ) { } SelectPointTool::~SelectPointTool() { } void SelectPointTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectPoints ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectPointTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectPointTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectPointTool::getPixmap() { return (const char **) selectpointtool_xpm; } const char * SelectPointTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectPointTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Points" ); } bool SelectPointTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_T; return true; } mm3d-1.3.15/src/tools/selectpointtool.h000066400000000000000000000034641466047437300200150ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTPOINTTOOL_H #define __SELECTPOINTTOOL_H #include "tool.h" #include "model.h" class BoundingBox; class SelectPointTool : public Tool { public: SelectPointTool(); virtual ~SelectPointTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; int m_startX; int m_startY; double m_x1; double m_y1; Matrix m_mat; Model::SelectionModeE m_selectionMode; }; #endif // __SELECTPOINTTOOL_H mm3d-1.3.15/src/tools/selectprojtool.cc000066400000000000000000000103061466047437300177650ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "menuconf.h" #include "selectprojtool.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectprojtool.xpm" #include #include #include #include SelectProjectionTool::SelectProjectionTool() : m_boundingBox( NULL), m_tracking( false ), m_unselect( false ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectProjections ) { } SelectProjectionTool::~SelectProjectionTool() { } void SelectProjectionTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectProjections ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->getModel()->setDrawProjections( true ); parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectProjectionTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectProjectionTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectProjectionTool::getPixmap() { return (const char **) selectprojtool_xpm; } const char * SelectProjectionTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectProjectionTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Projections" ); } bool SelectProjectionTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_T; return true; } mm3d-1.3.15/src/tools/selectprojtool.h000066400000000000000000000035001466047437300176250ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTPROJTOOL_H #define __SELECTPROJTOOL_H #include "tool.h" #include "model.h" class BoundingBox; class SelectProjectionTool : public Tool { public: SelectProjectionTool(); virtual ~SelectProjectionTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; int m_startX; int m_startY; double m_x1; double m_y1; Matrix m_mat; Model::SelectionModeE m_selectionMode; }; #endif // __SELECTPROJTOOL_H mm3d-1.3.15/src/tools/selectvertextool.cc000066400000000000000000000101451466047437300203310ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "config.h" #include "menuconf.h" #include "selectvertextool.h" #include "bounding.h" #include "decalmgr.h" #include "log.h" #include "modelstatus.h" #include "pixmap/selectvertextool.xpm" #include #include #include #include SelectVertexTool::SelectVertexTool() : m_boundingBox( NULL ), m_tracking( false ), m_unselect( false ), m_startX( 0 ), m_startY( 0 ), m_x1( 0.0 ), m_y1( 0.0 ), m_selectionMode( Model::SelectVertices ) { } SelectVertexTool::~SelectVertexTool() { } void SelectVertexTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { return; } parent->getModel()->setSelectionMode( Model::SelectVertices ); m_boundingBox = new BoundingBox(); DecalManager::getInstance()->addDecalToParent( m_boundingBox, parent ); if ( buttonState & BS_Right ) { m_unselect = true; } else { m_unselect = false; } m_tracking = true; m_startX = x; m_startY = y; m_x1 = 0.0; m_y1 = 0.0; parent->getRawParentXYValue( x, y, m_x1, m_y1 ); m_mat = parent->getParentViewMatrix(); if ( ! m_unselect && ! (buttonState & BS_Shift) ) { parent->getModel()->unselectAll(); } parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting selection" ).toUtf8().data() ); } void SelectVertexTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { if ( m_unselect ) { if ( buttonState & BS_Left ) { // We're waiting for the right button return; } } else { if ( buttonState & BS_Right ) { // We're waiting for the left button return; } } if ( m_tracking ) { m_tracking = false; double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; Model * model = parent->getModel(); parent->getRawParentXYValue( x, y, x2, y2 ); if ( m_unselect ) { model->unselectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } else { model->selectInVolumeMatrix( m_mat, x1, y1, x2, y2 ); } DecalManager::getInstance()->removeDecal( m_boundingBox ); m_boundingBox = NULL; parent->updateAllViews(); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Selection complete" ).toUtf8().data() ); } } void SelectVertexTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_tracking ) { double x1 = m_x1; double y1 = m_y1; double x2 = 0.0; double y2 = 0.0; parent->getRawParentXYValue( x, y, x2, y2 ); m_boundingBox->setMatrixBounds( m_mat, x1, y1, x2, y2 ); parent->updateView(); } } const char ** SelectVertexTool::getPixmap() { return (const char **) selectvertextool_xpm; } const char * SelectVertexTool::getPath() { return TOOLS_SELECT_MENU; } const char * SelectVertexTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Select Vertices" ); } bool SelectVertexTool::getKeyBinding( int arg, int & keyBinding ) { keyBinding = Qt::Key_V; return true; } mm3d-1.3.15/src/tools/selectvertextool.h000066400000000000000000000035161466047437300201770ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SELECTVERTEXTOOL_H #define __SELECTVERTEXTOOL_H #include "tool.h" #include "model.h" #include "glmath.h" class BoundingBox; class SelectVertexTool : public Tool { public: SelectVertexTool(); virtual ~SelectVertexTool(); int getToolCount() { return 1; }; const char * getName( int arg ); const char * getPath(); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: BoundingBox * m_boundingBox; bool m_tracking; bool m_unselect; int m_startX; int m_startY; double m_x1; double m_y1; Matrix m_mat; Model::SelectionModeE m_selectionMode; }; #endif // __SELECTVERTEXTOOL_H mm3d-1.3.15/src/tools/sheartool.cc000066400000000000000000000134041466047437300167170ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "sheartool.h" #include "model.h" #include "modelstatus.h" #include "pixmap/sheartool.xpm" #include "log.h" #include #include #include ShearTool::ShearTool() { } ShearTool::~ShearTool() { } const char * ShearTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Shear" ); } bool ShearTool::getKeyBinding( int arg, int & keyBinding ) { return false; } void ShearTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { m_positionCoords.clear(); Model * model = parent->getModel(); list posList; model->getSelectedPositions( posList ); ToolCoordList::iterator it; m_positionCoords.clear(); makeToolCoordList( parent, m_positionCoords, posList ); double cminX = 0; double cminY = 0; double cmaxX = 0; double cmaxY = 0; bool setFirst = false; for ( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { // update range if ( !setFirst ) { cminX = (*it).oldCoords[0]; cminY = (*it).oldCoords[1]; cmaxX = (*it).oldCoords[0]; cmaxY = (*it).oldCoords[1]; setFirst = true; } else { if ( (*it).oldCoords[0] < cminX ) { cminX = (*it).oldCoords[0]; } if ( (*it).oldCoords[0] > cmaxX ) { cmaxX = (*it).oldCoords[0]; } if ( (*it).oldCoords[1] < cminY ) { cminY = (*it).oldCoords[1]; } if ( (*it).oldCoords[1] > cmaxY ) { cmaxY = (*it).oldCoords[1]; } } } double curX = 0; double curY = 0; double minX = 0; double minY = 0; double maxX = 0; double maxY = 0; m_startLengthX = 0; m_startLengthY = 0; parent->getParentXYValue( x, y, curX, curY, true ); minX = fabs( cminX - curX ); minY = fabs( cminY - curY ); maxX = fabs( cmaxX - curX ); maxY = fabs( cmaxY - curY ); if ( minX > maxX ) { if ( minX > minY ) { if ( minX > maxY ) { m_axis = 1; m_far = cminX; m_orig = curY; } else { m_axis = 0; m_far = cmaxY; m_orig = curX; } } else { // minY > cminX if ( minY > maxY ) { m_axis = 0; m_far = cminY; m_orig = curX; } else { m_axis = 0; m_far = cmaxY; m_orig = curX; } } } else { // maxX > minX if ( maxX > minY ) { if ( maxX > maxY ) { m_axis = 1; m_far = cmaxX; m_orig = curY; } else { m_axis = 0; m_far = cmaxY; m_orig = curX; } } else { // minY > maxX if ( minY > maxY ) { m_axis = 0; m_far = cminY; m_orig = curX; } else { m_axis = 0; m_far = cmaxY; m_orig = curX; } } } m_startLengthX = distance( m_far, 0, curX, 0 ); m_startLengthY = distance( m_far, 0, curY, 0 ); model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Starting shear on selected primitives" ).toUtf8().data() ); } void ShearTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { double curX = 0; double curY = 0; parent->getParentXYValue( x, y, curX, curY ); ToolCoordList::iterator it; if ( m_axis == 0 ) { double offset = curX - m_orig; for( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { double x = (*it).oldCoords[0]; double y = (*it).oldCoords[1]; double z = (*it).oldCoords[2]; x = x + (offset * (fabs( y - m_far) / m_startLengthY) ); movePosition( parent, (*it).pos, x, y, z ); } } else { double offset = curY - m_orig; for( it = m_positionCoords.begin(); it != m_positionCoords.end(); it++ ) { double x = (*it).oldCoords[0]; double y = (*it).oldCoords[1]; double z = (*it).oldCoords[2]; y = y + (offset * (fabs(x - m_far) / m_startLengthX) ); movePosition( parent, (*it).pos, x, y, z ); } } parent->updateAllViews(); } void ShearTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { model_status( parent->getModel(), StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Shear complete" ).toUtf8().data() ); } const char ** ShearTool::getPixmap() { return (const char **) sheartool_xpm; } double ShearTool::distance( const double & x1, const double & y1, const double & x2, const double & y2 ) { double xDiff = x2 - x1; double yDiff = y2 - y1; return sqrt( xDiff*xDiff + yDiff*yDiff ); } double ShearTool::max( double a, double b ) { return ( a > b ) ? a : b; } mm3d-1.3.15/src/tools/sheartool.h000066400000000000000000000037021466047437300165610ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __SHEARTOOL_H #define __SHEARTOOL_H #include "tool.h" #include "model.h" #include using std::list; class ShearTool : public Tool { public: ShearTool(); virtual ~ShearTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool getKeyBinding( int arg, int & keyBinding ); bool isManipulation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); double distance( const double & x1, const double & y1, const double & x2, const double & y2 ); double min( double a, double b ); double max( double a, double b ); protected: double m_minX; double m_maxX; double m_maxY; double m_minZ; int m_axis; double m_far; double m_orig; double m_startLengthX; double m_startLengthY; ToolCoordList m_positionCoords; }; #endif // __SHEARTOOL_H mm3d-1.3.15/src/tools/toolwidget.cc000066400000000000000000000026071466047437300171030ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "toolwidget.h" ToolWidget::ToolWidget( QMainWindow * window, const QString & title ) : QDockWidget( title, window ), m_mainWidget( new QWidget( window ) ), m_layout( new QBoxLayout( QBoxLayout::LeftToRight, m_mainWidget ) ) { if ( title.isEmpty() ) setTitleBarWidget( new QWidget() ); setObjectName( "mainwin_toolwin" ); m_layout->setMargin( 5 ); setWidget( m_mainWidget ); window->addDockWidget( Qt::TopDockWidgetArea, this ); setFeatures( QDockWidget::NoDockWidgetFeatures ); } ToolWidget::~ToolWidget() { } mm3d-1.3.15/src/tools/toolwidget.h000066400000000000000000000025711466047437300167450ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2008 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef TOOLWIDGET_H_INC__ #define TOOLWIDGET_H_INC__ #include #include #include class ToolWidget : public QDockWidget { Q_OBJECT public: ToolWidget( QMainWindow * window, const QString & title = "" ); virtual ~ToolWidget(); protected: QBoxLayout * boxLayout() { return m_layout; } QWidget * mainWidget() { return m_mainWidget; } private: QWidget * m_mainWidget; QBoxLayout * m_layout; }; #endif // TOOLWIDGET_H_INC__ mm3d-1.3.15/src/tools/torustool.cc000066400000000000000000000140421466047437300167700ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "torustool.h" #include "pixmap/torustool.xpm" #include "model.h" #include "glmath.h" #include "log.h" #include "modelstatus.h" #include "glmath.h" #include "weld.h" #include #include #include #include using std::vector; using std::list; TorusTool::TorusTool() : m_segments( 8 ), m_sides( 8 ), m_width( 50 ), m_circle( false ), m_center( false ) { } TorusTool::~TorusTool() { } void TorusTool::activated( int arg, Model * model, QMainWindow * mainwin ) { log_debug( "torus activated\n" ); m_widget = new TorusToolWidget( this, mainwin ); m_widget->show(); } void TorusTool::deactivated() { m_widget->close(); } void TorusTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); model->unselectAll(); m_inverted = false; m_vertices.clear(); double pos[3] = {0,0,0}; parent->getParentXYValue( x, y, pos[0], pos[1], true ); m_startX = pos[0]; m_startY = pos[1]; double rx = 0.0; double ry = 0.0; double cx = 0.0; double cy = 0.0; double cz = 0.0; m_diameter = (double) m_width / 100.0; double rad = 1.0 - (m_diameter / 2.0); size_t triBase = model->getTriangleCount(); model->unselectAll(); for ( unsigned t = 0; t <= m_segments; t++ ) { double angle = (PI * 2) * ((double) t / (double) m_segments ); rx = cos( angle ); ry = sin( angle ); for ( unsigned n = 0; n <= m_sides; n++ ) { angle = (PI * 2) * ((double) n / (double) m_sides ); double c = cos( angle ) * m_diameter * 0.5; cz = sin( angle ); cx = (rx * rad) + (rx * c); cy = (ry * rad) + (ry * c); cx += 1.0; cy += 1.0; cx /= 2.0; cy /= 2.0; ToolCoordT v = addPosition( parent, Model::PT_Vertex, NULL, cx, cy, cz ); model->selectVertex( v.pos.index ); m_vertices.push_back( v ); } if ( t > 0 ) { unsigned vbase1 = model->getVertexCount() - ((m_sides+1) * 2); unsigned vbase2 = model->getVertexCount() - (m_sides+1); unsigned i; for ( i = 0; i < m_sides; i++ ) { model->addTriangle( vbase1 + i, vbase1 + i + 1, vbase2 + i ); model->addTriangle( vbase1 + i + 1, vbase2 + i + 1, vbase2 + i ); } } } size_t t = 0; size_t tcount = model->getTriangleCount(); for ( t = triBase; t < tcount; t++ ) { model->selectTriangle( t ); } updateDimensions( parent, 0, 0, 0 ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Torus created" ).toUtf8().data() ); } void TorusTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); weldSelectedVertices( model ); m_vertices.clear(); parent->updateAllViews(); } void TorusTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { double pos[3] = {0,0,0}; Model * model = parent->getModel(); parent->getParentXYValue( x, y, pos[0], pos[1] ); double xdiff = pos[0] - m_startX; double ydiff = pos[1] - m_startY; double zdiff = 0; bool invert = false; if ( m_circle ) { double diff = sqrt( fabs(xdiff * ydiff) ); ydiff = (ydiff < 0.0) ? -diff : diff; xdiff = (xdiff < 0.0) ? -diff : diff; } zdiff = ( fabs( xdiff ) > fabs( ydiff ) ) ? fabs( xdiff ) : fabs( ydiff ); zdiff *= (m_diameter * 0.25); updateDimensions( parent, xdiff, ydiff, zdiff ); if ( ( xdiff < 0.0 && ydiff < 0.0 ) || ( xdiff > 0.0 && ydiff > 0.0 ) ) { invert = true; } if ( invert != m_inverted ) { m_inverted = !m_inverted; size_t tcount = model->getTriangleCount(); for ( size_t t = 0; t < tcount; t++ ) { if ( model->isTriangleSelected( t ) ) { model->invertNormals( t ); } } } parent->updateAllViews(); } const char ** TorusTool::getPixmap() { return (const char **) torustool_xpm; } void TorusTool::updateDimensions( Tool::Parent * parent, double xdiff, double ydiff, double zdiff ) { ToolCoordList::iterator it = m_vertices.begin(); double centerX = m_startX; double centerY = m_startY; if ( m_center ) { centerX -= xdiff; centerY -= ydiff; xdiff *= 2.0; ydiff *= 2.0; zdiff *= 2.0; } while ( it != m_vertices.end() ) { movePosition( parent, (*it).pos, centerX + xdiff * (*it).oldCoords[0], centerY + ydiff * (*it).oldCoords[1], 0.0 + zdiff * (*it).oldCoords[2] ); it++; } } void TorusTool::setSegmentsValue( int newValue ) { m_segments = newValue; } void TorusTool::setSidesValue( int newValue ) { m_sides = newValue; } void TorusTool::setWidthValue( int newValue ) { m_width = newValue; } void TorusTool::setCircleValue( bool newValue ) { m_circle = newValue; } void TorusTool::setCenterValue( bool newValue ) { m_center = newValue; } const char * TorusTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Torus" ); } mm3d-1.3.15/src/tools/torustool.h000066400000000000000000000043611466047437300166350ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TORUSTOOL_H #define __TORUSTOOL_H #include "tool.h" #include "torustoolwidget.h" #include "toolpoly.h" #include class TorusTool : public Tool, public TorusToolWidget::Observer { public: TorusTool(); virtual ~TorusTool(); int getToolCount() { return 1; }; const char * getName( int arg ); void activated( int arg, Model * model, QMainWindow * mainwin ); void deactivated(); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); // TorusToolWidget::Observer void setSegmentsValue( int newValue ); void setSidesValue( int newValue ); void setWidthValue( int newValue ); void setCircleValue( bool newValue ); void setCenterValue( bool o ); protected: void updateDimensions( Tool::Parent * parent, double xdiff, double ydiff, double zdiff ); TorusToolWidget * m_widget; bool m_inverted; ToolCoordList m_vertices; unsigned m_segments; unsigned m_sides; unsigned m_width; bool m_circle; bool m_center; double m_startX; double m_startY; double m_diameter; }; #endif // __TORUSTOOL_H mm3d-1.3.15/src/tools/torustoolwidget.cc000066400000000000000000000126771466047437300202100ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "torustoolwidget.h" #include "3dmprefs.h" #include #include #include #include #include #include #include #include TorusToolWidget::TorusToolWidget( Observer * observer, QMainWindow * parent ) : ToolWidget ( parent ), m_observer( observer ) { const int DEFAULT_SEGMENTS = 8; const int DEFAULT_SIDES = 8; const int DEFAULT_WIDTH = 50; const bool DEFAULT_CIRCLE = false; m_layout = boxLayout(); m_segmentsLabel = new QLabel( tr("Segments"), mainWidget() ); m_layout->addWidget( m_segmentsLabel ); m_segmentsValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_segmentsValue ); m_segmentsValue->setMinimum( 3 ); m_segmentsValue->setMaximum( 100 ); int segmentsVal = DEFAULT_SEGMENTS; if ( g_prefs.exists( "ui_torustool_segments" ) ) { int val = g_prefs( "ui_torustool_segments" ).intValue(); if ( val >= 3 && val <= 100 ) { segmentsVal = val; } } m_segmentsValue->setValue( segmentsVal ); m_sidesLabel = new QLabel( tr("Sides"), mainWidget() ); m_layout->addWidget( m_sidesLabel ); m_sidesValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_sidesValue ); m_sidesValue->setMinimum( 3 ); m_sidesValue->setMaximum( 100 ); int sidesVal = DEFAULT_SIDES; if ( g_prefs.exists( "ui_torustool_sides" ) ) { int val = g_prefs( "ui_torustool_sides" ).intValue(); if ( val >= 3 && val <= 100 ) { sidesVal = val; } } m_sidesValue->setValue( sidesVal ); m_widthLabel = new QLabel( tr("Width"), mainWidget() ); m_layout->addWidget( m_widthLabel ); m_widthValue = new QSpinBox( mainWidget() ); m_layout->addWidget( m_widthValue ); m_widthValue->setMinimum( 1 ); m_widthValue->setMaximum( 199 ); int widthVal = DEFAULT_WIDTH; if ( g_prefs.exists( "ui_torustool_width" ) ) { int val = g_prefs( "ui_torustool_width" ).intValue(); if ( val >= 0 && val <= 100 ) { widthVal = val; } } m_widthValue->setValue( widthVal ); m_circleValue = new QCheckBox( tr("Circle"), mainWidget() ); m_layout->addWidget( m_circleValue ); bool circleVal = DEFAULT_CIRCLE; g_prefs.setDefault( "ui_torustool_circle", DEFAULT_CIRCLE ? 1 : 0 ); circleVal = g_prefs( "ui_torustool_circle" ).intValue() ? true : false; m_circleValue->setChecked( circleVal ); m_centerValue = new QCheckBox( tr("From Center", "Checkbox that indicates if torus is created from center or from far corner"), mainWidget() ); m_layout->addWidget( m_centerValue ); bool centerVal = DEFAULT_CIRCLE; g_prefs.setDefault( "ui_torustool_center", DEFAULT_CIRCLE ? 1 : 0 ); centerVal = g_prefs( "ui_torustool_center" ).intValue() ? true : false; m_centerValue->setChecked( centerVal ); m_layout->addStretch(); connect( m_segmentsValue, SIGNAL(valueChanged(int)), this, SLOT(segmentsValueChanged(int)) ); connect( m_sidesValue, SIGNAL(valueChanged(int)), this, SLOT(sidesValueChanged(int)) ); connect( m_widthValue, SIGNAL(valueChanged(int)), this, SLOT(widthValueChanged(int)) ); connect( m_circleValue, SIGNAL(toggled(bool)), this, SLOT(circleValueChanged(bool)) ); connect( m_centerValue, SIGNAL(toggled(bool)), this, SLOT(centerValueChanged(bool)) ); m_segmentsLabel->show(); m_segmentsValue->show(); m_sidesLabel->show(); m_sidesValue->show(); m_widthLabel->show(); m_widthValue->show(); m_circleValue->show(); m_centerValue->show(); segmentsValueChanged( segmentsVal ); sidesValueChanged( sidesVal ); widthValueChanged( widthVal ); circleValueChanged( circleVal ); centerValueChanged( centerVal ); } TorusToolWidget::~TorusToolWidget() { } void TorusToolWidget::segmentsValueChanged( int newValue ) { g_prefs( "ui_torustool_segments" ) = newValue; m_observer->setSegmentsValue( newValue ); } void TorusToolWidget::sidesValueChanged( int newValue ) { g_prefs( "ui_torustool_sides" ) = newValue; m_observer->setSidesValue( newValue ); } void TorusToolWidget::widthValueChanged( int newValue ) { g_prefs( "ui_torustool_width" ) = newValue; m_observer->setWidthValue( newValue ); } void TorusToolWidget::circleValueChanged( bool newValue ) { g_prefs( "ui_torustool_circle" ) = newValue ? 1 : 0; m_observer->setCircleValue( newValue ); } void TorusToolWidget::centerValueChanged( bool newValue ) { g_prefs( "ui_torustool_center" ) = newValue ? 1 : 0; m_observer->setCenterValue( newValue ); } mm3d-1.3.15/src/tools/torustoolwidget.h000066400000000000000000000044661466047437300200470ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __TORUSTOOLWIDGET_H #define __TORUSTOOLWIDGET_H class QMainWindow; class QVBoxLayout; class QHBoxLayout; class QBoxLayout; class QGroupBox; class QSpinBox; class QCheckBox; class QSlider; class QLabel; #include "toolwidget.h" class TorusToolWidget : public ToolWidget { Q_OBJECT public: class Observer { public: virtual ~Observer() {}; virtual void setSegmentsValue( int newValue ) = 0; virtual void setSidesValue( int newValue ) = 0; virtual void setWidthValue( int newValue ) = 0; virtual void setCircleValue( bool newValue ) = 0; virtual void setCenterValue( bool newValue ) = 0; }; TorusToolWidget( Observer * observer, QMainWindow * parent ); virtual ~TorusToolWidget(); public slots: void segmentsValueChanged( int newValue ); void sidesValueChanged( int newValue ); void widthValueChanged( int newValue ); void circleValueChanged( bool newValue ); void centerValueChanged( bool newValue ); protected: Observer * m_observer; QBoxLayout * m_layout; QGroupBox * m_groupBox; QLabel * m_segmentsLabel; QSpinBox * m_segmentsValue; QLabel * m_sidesLabel; QSpinBox * m_sidesValue; QLabel * m_widthLabel; QSpinBox * m_widthValue; QCheckBox * m_circleValue; QCheckBox * m_centerValue; }; #endif // __TORUSTOOLWIDGET_H mm3d-1.3.15/src/tools/vertextool.cc000066400000000000000000000045441466047437300171370ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include "vertextool.h" #include "model.h" #include "msg.h" #include "log.h" #include "modelstatus.h" #include "pixmap/vertextool.xpm" #include #include VertexTool::VertexTool() { m_vertex.pos.type = Model::PT_Point; } VertexTool::~VertexTool() { } void VertexTool::mouseButtonDown( Parent * parent, int buttonState, int x, int y ) { Model * model = parent->getModel(); double coord[3] = {0,0,0}; parent->getParentXYValue( x, y, coord[0], coord[1], true ); m_vertex = addPosition( parent, Model::PT_Vertex, NULL, coord[0], coord[1], coord[2] ); model->setVertexFree( m_vertex.pos.index, true ); model->unselectAll(); model->selectVertex( m_vertex.pos.index ); parent->updateAllViews(); model_status( model, StatusNormal, STATUSTIME_SHORT, "%s", qApp->translate( "Tool", "Vertex created" ).toUtf8().data() ); } void VertexTool::mouseButtonMove( Parent * parent, int buttonState, int x, int y ) { if ( m_vertex.pos.type == Model::PT_Vertex ) { double coord[3] = {0,0,0}; parent->getParentXYValue( x, y, coord[0], coord[1] ); movePosition( parent, m_vertex.pos, coord[0], coord[1], coord[2] ); parent->updateAllViews(); } } void VertexTool::mouseButtonUp( Parent * parent, int buttonState, int x, int y ) { } const char ** VertexTool::getPixmap() { return (const char **) vertextool_xpm; } const char * VertexTool::getName( int arg ) { return QT_TRANSLATE_NOOP( "Tool", "Create Vertex" ); } mm3d-1.3.15/src/tools/vertextool.h000066400000000000000000000027151466047437300167770ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #ifndef __VERTEXTOOL_H #define __VERTEXTOOL_H #include "tool.h" class VertexTool : public Tool { public: VertexTool(); virtual ~VertexTool(); int getToolCount() { return 1; }; const char * getName( int arg ); bool isCreation() { return true; }; void mouseButtonDown( Parent * parent, int buttonState, int x, int y ); void mouseButtonUp( Parent * parent, int buttonState, int x, int y ); void mouseButtonMove( Parent * parent, int buttonState, int x, int y ); const char ** getPixmap(); protected: ToolCoordT m_vertex; }; #endif // __VERTEXTOOL_H mm3d-1.3.15/src/win_manifest.xml000066400000000000000000000032411466047437300164530ustar00rootroot00000000000000 True/PM PerMonitorV2, PerMonitor mm3d-1.3.15/src/win_resource.rc000066400000000000000000000044641466047437300163100ustar00rootroot00000000000000/* Maverick Model 3D * * Copyright (c) 2004-2007 Kevin Worcester * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ #include A ICON "mm3d.ico" CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "win_manifest.xml" #define VER_PRODUCTVERSION 1,3,15,0 #define VER_PRODUCTVERSION_STR "1.3.15\0" #ifndef DEBUG #define VER_DEBUG 0 #else #define VER_DEBUG VS_FF_DEBUG #endif VS_VERSION_INFO VERSIONINFO FILEVERSION VER_PRODUCTVERSION PRODUCTVERSION VER_PRODUCTVERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS (VER_DEBUG) FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "ProductName", "Maverick Model 3D" VALUE "FileDescription", "Maverick Model 3D" VALUE "ProductVersion", VER_PRODUCTVERSION_STR VALUE "FileVersion", VER_PRODUCTVERSION_STR #ifdef _WIN64 VALUE "OriginalFilename", "mm3d.x86_64.exe" VALUE "InternalName", "mm3d.x86_64" #else VALUE "OriginalFilename", "mm3d.x86.exe" VALUE "InternalName", "mm3d.x86" #endif // Copyright symbol (©) in Windows codepage 1252 is \xA9 VALUE "LegalCopyright", "Copyright \xA9 2004-2008 Kevin Worcester, Copyright \xA9 2009-2024 Zack Middleton." END END BLOCK "VarFileInfo" BEGIN /* English language (0x409) in the Windows ANSI codepage (1252). */ VALUE "Translation", 0x409, 1252 END END mm3d-1.3.15/translations/000077500000000000000000000000001466047437300152005ustar00rootroot00000000000000mm3d-1.3.15/translations/Makefile.am000066400000000000000000000012271466047437300172360ustar00rootroot00000000000000QMFILES = \ mm3d_de.qm \ mm3d_fr.qm \ mm3d_sk.qm \ mm3d_ref.qm \ mm3d_bork.qm TSFILES = \ $(srcdir)/mm3d_ref.ts \ $(srcdir)/mm3d_de.ts \ $(srcdir)/mm3d_fr.ts \ $(srcdir)/mm3d_sk.ts \ $(srcdir)/mm3d_bork.ts EXTRA_DIST = \ $(QMFILES) \ $(TSFILES) BUILT_SOURCES = $(QMFILES) all: tsfiles: lupdate $(srcdir)/../src/*.cc $(srcdir)/../src/*/*.cc $(srcdir)/../src/*.h $(srcdir)/../src/*/*.h $(srcdir)/../src/qtui/*.ui -ts $(TSFILES) qmfiles: $(QMFILES) %.qm: %.ts $(QT_LRELEASE) $< -qm $@ install: $(INSTALL) -d $(DESTDIR)$(datadir)/mm3d/translations ${INSTALL} -m 0644 $(QMFILES) $(DESTDIR)$(datadir)/mm3d/translations CLEANFILES = $(QMFILES) mm3d-1.3.15/translations/mm3d_bork.ts000066400000000000000000006027551466047437300174440ustar00rootroot00000000000000 AboutWin Maverick Model 3D - About Bork! AlignWin F1 Help Shortcut Bork! Align X Bork! Align Y Bork! Align Z Bork! Align Selected operation complete Bork! AlignWinBase Align Selection Bork! Align X Bork! Align minimum Bork! Align center Bork! Align maximum Bork! Align &X Now Bork! Alt+X Bork! Align Y Bork! Align &Y Now Bork! Alt+Y Bork! Align Z Bork! Align &Z Now Bork! Alt+Z Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! AnimConvertWinBase Convert To Frame Bork! Convert Skeletal to Frame: Bork! example Skeletal Animation Bork! Frame Animation Bork! Convert AnimName Bork! Frame Anim Name: Bork! Frame Count Bork! TORSO_IDLE 1 Bork! {1?} F1 for help Bork! Continue Bork! Cancel Bork! Cancel All Bork! AnimConvertWindow F1 Help Shortcut Bork! Convert Skeletal to Frame: Bork! Skeletal Animation Bork! Convert Frame to Frame: Bork! Frame Animation Bork! Convert Frame Relative to Frame: Bork! Frame Relative Animation Convert Unknown Type to Frame: Bork! Unknown Type Animation AnimExportWinBase Export Animation Bork! Source Bork! Animation Bork! Viewport Bork! Duration Bork! Iterations Bork! 10 Bork! Seconds Bork! 60 Bork! Output Bork! Directory Bork! Format Bork! anim_0001.jpg Bork! anim_1.jpg Bork! anim_0001.png Bork! anim_1.png Bork! ... Bork! Frame Rate Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! AnimExportWindow Skeletal - Skeletal Animation prefix Bork! Frame - Frame Animation prefix Bork! [None] No viewport for animation image export Bork! Viewport %1 - Bork! F1 Help Shortcut Bork! Must have more than 0 frames per second Bork! Must have more than 0 seconds of animation Bork! Could not write file: Bork! Output directory does not exist. Bork! AnimSetWinBase Animation Sets Bork! &Down Bork! &Up Bork! &New Bork! &Rename Bork! D&elete Bork! &Copy Bork! &Split Bork! &Join Bork! &Merge Bork! Con&vert To Frame Animation Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! AnimSetWindow Skeletal Animation Bork! Frame Animation Bork! F1 Help Shortcut Bork! New Animation Bork! New name: Bork! Rename Animation Bork! copy Bork! Split at frame Split animation frame window title Bork! Split 'Split' refers to splitting an animation into two separate animations Bork! at frame number the frame number where the second (split) animation begins Bork! split Bork! Cannot Split Cannot split animation window title Bork! Must have at least 2 frames to split split animation Bork! Cannot merge animation %1 and %2, frame counts differ. Bork! Can only merge skeletal animations. Bork! Animation changes operation complete Bork! AnimWidget F1 Help Shortcut Bork! <New Animation> Bork! Start animation mode operation complete Bork! New Animation operation complete Bork! Frame: Bork! Set FPS Frames per second, operation complete Bork! Change Frame Count operation complete Bork! Clear frame Remove animation data from frame, operation complete Bork! Paste frame paste frame animation position, operation complete Bork! Paste keyframe Paste keyframe animation data complete Bork! Set Looping Change whether animation loops operation complete End animation mode operation complete Bork! Frame: n/a Bork! Delete Animation? window title Are you sure you want to delete this animation? Delete Animation Delete animation, operation complete No frame animation data to paste No skeletal animation data to paste AnimWidgetBase Animation Bork! FPS Bork! Play Bork! Stop Bork! Loop Bork! Frames Bork! X Bork! Delete Animation AnimWinBase Animation Bork! New Bork! Rename Bork! Delete Bork! Keyframe Bork! Frames Bork! FPS Bork! Frame: 03 Bork! Copy Frame Bork! Paste Frame Bork! Clear Frame Bork! Skeletal Bork! Frame Bork! Play Bork! Stop Bork! Loop Bork! Press F1 for help Bork! Close Bork! AnimWindow F1 Help Shortcut Bork! Misfit 3D Bork! New name: Bork! Animations Bork! AutoAssignJointWin F1 Help Shortcut Bork! AutoAssignJointWinBase Auto-Assign Bone Joints Bork! Only assign to selected joints Bork! Single Bork! Multiple Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! BackgroundSelect All Supported Formats ( All Borked Formats ( Open background image Bork! All Files (*) All Bork! (*) Could not open file Bork! BackgroundSelectBase None Bork! File... Bork! BackgroundWin F1 Help Shortcut Bork! Background Image operation complete Bork! BackgroundWinBase Select Background Image Bork! Front Bork! Back Bork! Left Bork! Right Bork! Top Bork! Bottom Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! BoolPanel Boolean Operation Bork! BoolWin Union boolean operation Bork! Subtraction boolean operation Bork! Intersection boolean operation Bork! You must have at least once face selected Bork! Object A triangles are still selected Bork! Union With Selected boolean operation Bork! Subtract Selected boolean operation Bork! Intersect With Selected boolean operation Bork! Fuse Selected boolean operation Bork! Select faces to set Select faces to set as 'A' Object in boolean operation Bork! BoolWinBase Boolean Operation Bork! Operation Bork! Fuse Bork! Union Bork! Subtraction Bork! Intersection Bork! Set Object A Bork! Select faces to set Bork! Subtract Selected Bork! Cal3dPrompt F1 Help Shortcut Bork! Cal3dPromptBase Cal3D Filter Options Save Meshes All meshes in one file Group meshes by name Save Materials CRF (Binary) XRF (XML Text) Ok Bork! Cancel Bork! Command Align Selected... Bork! Cap Holes complete Bork! Could not find gap in selected region Bork! Cap Holes Bork! Selected primitives copied Bork! You must have at least 1 face, joint, or point selected to Copy Bork! Copy complete Bork! Copy Selected to Clipboard Bork! Delete Bork! Deleting joints may destroy skeletal animations Do you wish to continue? Bork! Primitives deleted Bork! Selected primitives duplicated Bork! You must have at least 1 face, joint, or point selected to Duplicate Bork! Duplicate complete Bork! Duplicate Bork! Edge Divide complete Bork! You must have 2 adjacent vertices selected to Edge Divide Bork! Edge Divide Bork! Edge Turn complete Bork! You must have at least 2 adjacent faces to Edge Turn Bork! Edge Turn Bork! Extrude... Bork! Flatten Bork! Flatten X Bork! Flatten Y Bork! Flatten Z Bork! Need at least 1 vertex, joint, point, or face selected Bork! Selected primitives flattened Bork! Flip Bork! Flip X Bork! Flip Y Bork! Flip Z Bork! Selected primitives flipped Bork! Hide Bork! Hide Unselected Bork! Hide Selected Bork! Unhide All Bork! Selected primitives hidden Bork! Primitives unhidden Bork! Unselected primitives hidden Bork! Selection inverted Bork! Invert Selection Bork! Normals inverted Bork! Invert Normals Bork! Make Face From Vertices Bork! Face created Bork! Must select exactly 3 vertices Bork! Paste complete Bork! Nothing to paste Paste failed: %1 %2 Paste from Clipboard Bork! Rotate Texture Coordinates Bork! Face Bork! Group Bork! Texture coordinates rotated Bork! Must select faces Bork! Select Free Vertices Bork! Free-floating vertices selected Bork! Simplify Mesh Bork! Snap Vertices Together Bork! Snap All Selected Bork! Snap Nearest Selected Bork! Snap All and Weld Bork! Snap Nearest and Weld Bork! Spherify... Bork! Subdivide complete Bork! You must have at least 1 face selected to Subdivide Faces Subdivide Faces Bork! You must have 1 or more vertices selected to unweld. Bork! Unweld Vertices Unbork borks! You must have 2 or more vertices selected to weld. Bork! Weld Vertices Bork borks! Unwelded %1 vertices into %2 vertices Unborked %1 borks into %2 borks Welded %1 vertices into %2 vertices Borked %1 borks into %2 borks! Normals face out Bork! Vertices Bork! Faces Bork! Meshes Bork! Normals Bork! Normals Face Out CommandWidget You are in animation mode, but there are no animations ContextGroup <None> Bork! <New> Bork! New Group Name of new group, window title Bork! Enter new group name: Bork! Set Group operation complete Bork! Unset Group operation complete Bork! Set Material operation complete Bork! Set Projection operation complete Bork! ContextGroupBase Group Bork! Projection Name Bork! ... Bork! Material Name Bork! Group Material: Bork! Group Name Bork! Triangle Projection Bork! Texture Projection Bork! ContextInfluences <None> Bork! Change Joint Assignment operation complete Bork! Change Influence Weight operation complete Bork! Change Influence Type operation complete Bork! <Mixed> multiple types of bone joint influence Bork! Custom bone joint influence Bork! Auto bone joint influence Bork! Remainder bone joint influence Bork! Auto: %1 Bork! Rem: %1 Bork! ContextInfluencesBase Weight Bork! <Mixed> Bork! Custom Bork! Auto Bork! Remaining Bork! <None> Bork! Joint Bork! ContextName Rename operation complete Bork! ContextNameBase Name Bork! ContextPanel Properties Window title Bork! ContextPosition Set Position operation complete Bork! ContextPositionBase Position Bork! Z Bork! Y Bork! X Bork! Dimensions ContextProjection Set Projection Type operation complete Bork! ContextProjectionBase Projection Type Bork! Cylinder Bork! Sphere Bork! Plane Bork! ... Bork! ContextRotation Set Rotation operation complete Bork! ContextRotationBase Rotation Bork! Z Bork! Y Bork! X Bork! CubeToolWidget Cube Bork! Segment Bork! CylinderToolWidget Segments Bork! Sides Bork! Width Bork! Scale Bork! DeleteCommand Deleting joints may destroy skeletal animations Do you wish to continue? Bork! Primitives deleted Bork! EditKeyframeWin Set keyframe %1 Bork! EllipsoidToolWidget Smoothness: Bork! Faces: Bork! Sphere Bork! From Center Checkbox that indicates if ellipsoid is created from center or far corner Bork! ErrorObject Success Bork! Canceled Bork! File is an unknown type Bork! Operation not supported for this file type Bork! Invalid argument (internal error) Bork! File does not exist Bork! Permission denied Bork! Could not open file Bork! Could not read from file Bork! File is the wrong type or corrupted Bork! Unsupported version Bork! File contains invalid data Bork! Unexpected end of file Bork! Unknown error Bork! Could not write file Bork! This operation is not supported Bork! Write not supported, try "Export..." Bork! Unrecognized file extension (unknown type) ExtrudeWin F1 Help Shortcut Bork! Extrude complete Bork! Extrude operation complete Bork! ExtrudeWinBase Extrude Bork! Extrude options Bork! X: Bork! Z: Bork! Y: Bork! Make Back Faces Bork! E&xtrude Bork! Press F1 for help Bork! Close Bork! GroupCleanBase Clean Up Merge identical materials Remove unused materials Merge identical groups Remove unused groups Ok Bork! Cancel Bork! GroupCleanWin F1 Help Shortcut Bork! Group Clean-up operation complete Merged %1 groups, %2 materials; Removed %3 of %4 groups, %5 of %6 materials GroupWinBase Groups Bork! No group Bork! New Bork! Rename Bork! Delete Bork! Select Faces In Group Bork! Unselect Faces In Group Bork! Faces Bork! Assign As Group Bork! Add To Group Bork! Texture Bork! No texture Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! GroupWindow F1 Help Shortcut Bork! New group window title Bork! Enter new group name: Bork! Group name must be between 1 and %1 characters Bork! Bad group name window title Bork! Cannot change cannot change group name, window title Bork! You cannot change the default group name Bork! Smoothness: Bork! Max Angle: Bork! Group changes operation complete Bork! HelpWinBase Help Bork! Contents Bork! Back Bork! Forward Bork! Ok Bork! IqePrompt F1 Help Shortcut Bork! IqePromptBase IQE Filter Options Save Meshes Save Points as Bone Joints Save Skeleton Save Animations Press F1 for help Bork! Ok Bork! Cancel Bork! JointWin F1 Help Shortcut Bork! Rename joint window title Bork! Enter new joint name: Bork! Joint changes operation complete Bork! JointWinBase Joints Bork! None Bork! Rename Bork! Delete Bork! Selection Bork! Select Joint Vertices Bork! Select Unassigned Vertices Bork! Assign Selected to Joint Bork! Add Selected to Joint Bork! F1 for help Bork! Ok Bork! Cancel Bork! KeyConfig V Select Vertices Tool Shortcut V F Select Faces Tool Shortcut F C Select Connected Mesh Tool Shortcut C G Select Groups Tool Shortcut G B Select Bone Joints Tool Shortcut B T Select Points Tool Shortcut T M Move Tool Shortcut M R Rotate Tool Shortcut R H Hide Unselected Command Shortcut H Shift+H Hide Selected Command Shortcut Shift+H ? Unhide All Command Shortcut ? Delete Delete Command Shortcut Delete Ctrl+D Duplicate Command Shortcut Ctrl+D Ctrl+C Copy to Clipboard Command Shortcut Ctrl+C Ctrl+V Paste from Clipboard Command Shortcut Ctrl+V Insert Extrude Command Shortcut Insert Ctrl+W Weld Command Shortcut Ctrl+W Ctrl+N File | New Window Shortcut Ctrl+N Ctrl+O File | Open Shortcut Ctrl+O Ctrl+S File | Save Shortcut Ctrl+S Ctrl+Q File | Quit Shortcut Ctrl+Q Home View | Frame All Shortcut Home Shift+Home View | Frame Selected Shortcut Shift+Home Ctrl+G Groups | Edit Groups Shortcut Ctrl+G Ctrl+M Groups | Edit Materials Shortcut Ctrl+M Ctrl+E Groups | Edit Texture Coordinates Shortcut Ctrl+E Ctrl+B Joints | Assign Selected Shortcut Ctrl+B Shift+U Unhide All Command Shortcut KeyValueWindowBase Edit Meta Data Bork! Name Bork! Value Bork! Ok Bork! Cancel Bork! LicenseWin GNU General Public License Bork! LowLevel Cannot delete root joint Bork! Cannot add or delete because you have frame animations. Try "Merge..." instead. Bork! Success Bork! Canceled Bork! File is an unknown type Bork! Operation not supported for this file type Bork! Invalid argument (internal error, probably null pointer argument) Bork! File does not exist Bork! Permission denied Bork! Could not open file Bork! Could not read from file Bork! File is the wrong type or corrupted Bork! Unsupported version Bork! File contains invalid data Bork! Unexpected end of file Bork! Unknown error Bork! Invalid error code Bork! Could not write file Bork! This operation is not supported Bork! MM3D encountered an unexpected data size problem See Help->About to contact the developers Bork! The model has a texture that's width or height is not a power of two (2, 4, 8, .., 64, 128, 256, ..). Bork! Could not load Bork! Model contains no skeletal animations Bork! Model skeletons do not match Bork! This looks like a player model. Do you want to load all sections? Bork! Could not load texture Bork! Write not supported, try "Export..." Bork! Unrecognized file extension (unknown type) MD2 requires all groups to have the same material. MD2 export requires all faces to be grouped. MD3 export requires all faces to be grouped. MD3_PATH+filename is to long. Set meta data for MD3 export Point name is too large for MD3 export. Group name is too large for MD3 export. Texture filename is too long. MM3D does not support CAL3D files in XML format The file does not contain any mesh or animation data Set meta data for Cal3D export No data marked for saving as IQE. IQE requires all faces to be grouped. IQE requires points to only have one bone influence. Too many vertexes for MS3D export (max 65,536). Too many faces for MS3D export (max 65,536). Too many groups for MS3D export (max 255). Too many materials for MS3D export (max 255). Too many bone joints for MS3D export (max 255). Bone joints must have unique names for MS3D export. Too many vertexes for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Too many faces for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Set meta data for MS3D export Bone joints must have unique names for SMD export. SMD export requires points to only have one bone influence. Set meta data for SMD export Unsupported D3D version. Missing version in D3D model. Invalid D3D directives count. Missing line count in D3D model. Too few tokens on line in D3D model. Found primitive start without primitive end in D3D model. Line primitive type is not supported for D3D model. Triangle strip primitive type is not supported for D3D model. Triangle fan primitive type is not supported for D3D model. Unsupported primitive type in D3D model. Primitive end without start in D3D model. Incomplete triangle list before primitive end in D3D model. Vertex outside of primitive begin/end in D3D model. Primitive start without end in D3D model. D3D requires all groups to have the same material. MapDirectionBase Which direction? Bork! Set new texture coordinates from which direction? Bork! Front Bork! Back Bork! Left Bork! Right Bork! Top Bork! Bottom Bork! Ok Bork! Cancel Bork! Md3Prompt F1 Help Shortcut Bork! Md3PromptBase MD3 Filter Options Save as a Player Model (head.md3, upper.md3, lower.md3) Save animation.cfg Press F1 for help Bork! Ok Bork! Cancel Bork! MergeWinBase Merge Model Bork! Merge location Bork! Base Point <origin> Rotation Bork! Translation Bork! Merge Options Bork! Include textures Bork! Include animations Bork! Animation Options Bork! Append animations Bork! Merge if possible Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! MergeWindow F1 Help Shortcut Bork! Merge models operation complete Bork! MetaWindow F1 Help Shortcut Bork! Name meta value key name Bork! Value meta value 'value' Bork! Change meta data operation complete Bork! MetaWindowBase Name Bork! Value Bork! Model Meta Data Bork! New Bork! Delete Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! ModelViewBase ModelView Bork! Perspective Bork! Front Bork! Back Bork! Left Bork! Right Bork! Top Bork! Bottom Bork! Orthographic Bork! ModelViewport Could not load background %1 Bork! Use the middle mouse button to drag/pan the viewport Bork! OpenGL error = Invalid Value Bork! OpenGL error = Invalid Enum Bork! OpenGL error = Invalid Operation Bork! OpenGL error = Stack Overflow Bork! OpenGL error = Stack Underflow Bork! OpenGL error = Out Of Memory Bork! OpenGL error = Unknown Bork! Ms3dPrompt F1 Help Shortcut Bork! Ms3dPromptBase MS3D Filter Options Vertex Format Subversion 0 (Single bone joint influence) Subversion 1 (Multiple bone joints influences, weight scale 255) Subversion 2 (Multiple bone joints influences, weight scale 100) Subversion 3 (Multiple bone joints influences, weight scale 100) Subversion Options Vertex Extra Vertex Extra 2 Press F1 for help Bork! Ok Bork! Cancel Bork! NewAnimBase New Animation Bork! &Name Bork! Animation Type Bork! &Skeletal Bork! Alt+S Bork! &Frame Bork! Alt+F Bork! &Ok Bork! Alt+O Bork! &Cancel Bork! Alt+C Bork! ObjPrompt F1 Help Shortcut Bork! ObjPromptBase OBJ Filter Options Bork! &Save normals Bork! &Save Normals Alt+S Bork! &Normal Decimal Places Bork! &Texture Decimal Places Bork! &Vertex Decimal Places Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! PaintTextureWin F1 Help Shortcut Bork! File name for saved texture? Bork! File exists. Overwrite? Bork! Could not write file: Bork! PaintTextureWinBase Paint Texture Bork! Polygons: Bork! Edges Bork! Filled Bork! Filled and Edges Bork! Vertices Bork! Hidden Bork! Visible Bork! Clear Background Bork! Save Size: Bork! 64 Bork! 128 Bork! 256 Bork! 512 Bork! 1024 Bork! 2048 Bork! x Bork! Save Texture... Bork! Press F1 for help Bork! Close Bork! PluginWinBase Plugin Bork! Version Bork! Description Bork! Status Bork! Plugins Bork! Filename Press F1 for help Bork! Ok Bork! PluginWindow F1 Help Shortcut Bork! PointWin F1 Help Shortcut Bork! Rename point window title Bork! Enter new point name: Bork! Point changes operation complete Bork! PointWinBase Points Bork! Rename Bork! Delete Bork! Bone Joint Bork! (none) Bork! F1 for help Bork! Ok Bork! Cancel Bork! PolyToolWidget Fan Bork! Poly Type Strip Triangle strip option Fan Triangle fan option Bork! ProjToolWidget Type Cylinder Cylinder projection type Bork! Sphere Sphere projection type Bork! Plane Plane projection type Bork! ProjectionWin F1 Help Shortcut Bork! Ctrl+Z Undo Ctrl+Z Ctrl+Y Redo Ctrl+Y Set Projection Type operation complete Bork! Set Triangle Projection operation complete Bork! Apply Projection operation complete Bork! Reset UV Coordinates operation complete Bork! Rename projection window title Bork! Enter new point name: Bork! Rename Projection operation complete Bork! CTRL+Z Undo shortcut CTRL+Y Redo shortcut ProjectionWinBase Texture Projection Bork! Material Bork! Test Pattern Bork! Cylinder Bork! Sphere Bork! Plane Bork! Show: Bork! Type: Bork! Rename Bork! Zoom: Bork! Remove Faces Bork! Add Faces to Projection Bork! Apply Projection Bork! Reset UV Range Bork! Press F1 for help Bork! Close Bork! RgbaWin F1 Help Shortcut Bork! RgbaWinBase RGBA Window Bork! Light Property Bork! Red Bork! Green Bork! Blue Bork! Alpha Bork! Close Bork! RotateToolWidget X Bork! Y Bork! Z Bork! ScaleToolWidget Proportion Bork! Free Free scaling option Bork! Keep Aspect 2D 2D scaling aspect option Bork! Keep Aspect 3D 3D scaling aspect option Bork! Point Bork! Center Scale from center Bork! Far Corner Scale from far corner Bork! SelectFaceToolWidget Include Back-facing Bork! SmdPrompt F1 Help Shortcut Bork! SmdPromptBase SMD Filter Options Model Type Reference Animation Bork! Save Points as Bone Joints Vertex Format GoldSrc (Single bone joint influence) Source (Multiple bone joint influences) Press F1 for help Bork! Ok Bork! Cancel Bork! SpherifyWin Spherify operation complete Bork! StartPrompt F1 Help Shortcut Bork! StatusBar V: Vertices status bar label Bork! F: Faces status bar label Bork! G: Groups status bar label Bork! B: Bone Joints status bar label Bork! P: Points status bar label Bork! M: Materials status bar label Bork! TextWinBase Ok Bork! TextureCoord Reset coordinates? window title Bork! Are you sure you want to reset texture coordinates for this group? Bork! Move texture coordinates Bork! F1 Help Shortcut Bork! CTRL+Z Undo shortcut CTRL+Y Redo shortcut Select texture coordinates TextureCoordBase Texture Coordinates Bork! Zoom: Bork! Mouse Tool Bork! Select Bork! Move Bork! Scale Bork! Scale Options Bork! Scale from center Bork! Keep aspect ratio Bork! Map Scheme Bork! Triangle Bork! Quad Bork! Group Bork! Reset Coordinates Bork! Press F1 for help Bork! Close Bork! Rotate Bork! Lines Black Blue Bork! Green Bork! Cyan Red Bork! Magenta Yellow White Selection Bork! Rotate CCW Rotate CW V Flip H Flip TextureWindow All Supported Formats ( all texture formats All Borked Formats ( Open texture image Bork! All Files (*) All Bork! (*) Could not open file Bork! Color Material window title Bork! Enter new material name: Bork! Rename texture window title Bork! Enter new texture name: Bork! New Material window title Rename material window title Texture changes Bork! Shininess Bork! Red Bork! Change texture... Change material's texture file Bork! Set texture... Add texture file to material Bork! F1 Help Shortcut Bork! TextureWindowBase Materials Bork! None Bork! New Material... Bork! Rename Bork! Delete Bork! Wrap X Bork! Clamp X Bork! Wrap Y Bork! Clamp Y Bork! Change Texture... Bork! X Bork! Remove texture Bork! Flat Preview Bork! 3D Preview Bork! Alpha Bork! Green Bork! Blue Bork! Red Bork! Ambient Bork! Diffuse Bork! Specular Bork! Emissive Bork! Shininess Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! Tool Attract Far Bork! Attracting far selected primitives Bork! Attract far complete Bork! Attract Near Bork! Attracting near selected primitives Bork! Attract near complete Bork! Move Background Image Bork! Moving background image Bork! Cannot move background from 3D view Bork! Background move complete Bork! Move background image Bork! Scale Background Image Bork! Scaling background image Bork! Cannot scale background from 3D view Bork! Background scale complete Bork! Scale background image Bork! Cube created Bork! Create Cube Bork! Cylinder created Bork! Create Cylinder Bork! Ellipsoid created Bork! Create Ellipsoid Bork! Joint created Bork! Root joint created Bork! Create Bone Joint Bork! Moving selected primitives Bork! Move complete Bork! Tip: Hold shift to restrict movement to one dimension Bork! Move Bork! Point created Bork! Create Point Bork! Create Polygon Bork! Projection created Bork! Create Projection Bork! Rectangle created Bork! Create Rectangle Bork! Tip: Hold shift to rotate in 15 degree increments Bork! Rotating selected primitives Bork! Setting rotation point Bork! Rotate complete Bork! Rotate Bork! Scale Bork! Scaling selected primitives Bork! Scale complete Bork! Tip: Hold shift to restrict scaling to one dimension Bork! Starting selection Bork! Selection complete Bork! Select Bone Joints Bork! Select Connected Mesh Bork! Select Faces Bork! Select Groups Bork! Select Points Bork! Select Projections Bork! Select Vertices Bork! Shear Bork! Starting shear on selected primitives Bork! Shear complete Bork! Torus created Bork! Create Torus Bork! Vertex created Bork! Create Vertex Bork! Dragging selected vertex Bork! Must a vertex selected Bork! Drag complete Bork! Drag Vertex on Edge Bork! Extrude complete Bork! Extrude Bork! Must have faces selected to extrude Bork! Extruding selected faces Bork! Select Bork! Attract Bork! Background Image Bork! Create Other Bork! TorusToolWidget Segments Bork! Sides Bork! Width Bork! Circle Bork! From Center Checkbox that indicates if torus is created from center or from far corner Bork! TransformWindow Matrix Translate Bork! Matrix Rotate Bork! Matrix Rotate On Axis Bork! Matrix Scale Bork! Apply Matrix Bork! Transform Cannot Be Undone window title Bork! This transformation cannot be undone. Bork! Are you sure you wish to continue? Bork! Apply Transformation button Bork! Cancel Transformation button Bork! F1 Help Shortcut Bork! TransformWindowBase Transform Model Bork! X Bork! Y Bork! Z Bork! Translate Bork! Euler Angles Bork! Rotate Bork! Quaternion Bork! Angle Bork! Axis Bork! Scale Bork! (bottom row is translation) Bork! Apply Matrix Bork! Matrix Bork! Apply to: Bork! Entire Model and Animations Bork! Press F1 for help Bork! Close Bork! Selected (including animations) Entire Model (including animations) ValueWin F1 Help Shortcut Bork! ValueWinBase Value Window Bork! Value Bork! Press F1 for help Bork! Ok Bork! Cancel Bork! ViewWindow Some models are unsaved. Save before exiting? Bork! Press F1 for help using any window Bork! Animations Bork! Properties Bork! New File|New Bork! Open File|Open Bork! Save File|Save Bork! Save As File|Save As Bork! Set Background Image... File|Set Background Image Bork! Run Script... File|Run Script Bork! Recent Scripts File|Recent Script Bork! Merge... File|Merge Bork! Merge Animations... File|Merge Animations Bork! Recent Models File|Recent Models Bork! Plugins... File|Plugins Bork! Close File|Close Bork! Quit File|Quit Bork! Hide Joints View|Hide Joints Bork! Draw Joint Lines View|Draw Joint Lines Bork! Draw Joint Bones View|Draw Joint Bones Bork! Draw Texture Projections View|Draw Texture Projections Bork! Hide Texture Projections View|Hide Texture Projections Bork! Use Red Error Texture View|Use Red Error Texture Bork! Use Blank Error Texture View|Use Blank Error Texture Bork! Render 3D Lines View|Render 3D Lines Bork! Hide 3D Lines View|Hide 3D Lines Bork! Draw Back-facing Triangles View|Draw Back-facing Triangles Bork! Hide Back-facing Triangles View|Hide Back-facing Triangles Bork! Frame All View|Frame Bork! Frame Selected View|Frame Bork! Show Properties View|Show Properties Bork! Render Options View|Render Options Bork! 3D Wireframe View|3D Bork! 3D Flat View|3D Bork! 3D Smooth View|3D Bork! 3D Texture View|3D Bork! 3D Alpha Blend View|3D Bork! Canvas Wireframe View|Canvas Bork! Canvas Flat View|Canvas Bork! Canvas Smooth View|Canvas Bork! Canvas Texture View|Canvas Bork! Canvas Alpha Blend View|Canvas Bork! 1 View View|Viewports Bork! 1x2 View View|Viewports Bork! 2x1 View View|Viewports Bork! 2x2 View View|Viewports Bork! 2x3 View View|Viewports Bork! 3x2 View View|Viewports Bork! 3x3 View View|Viewports Bork! Viewport Settings... View|Viewport Settings Bork! Grid Tools|Snap to Grid Bork! Vertex Tools|Snap to Vertex Bork! Undo Bork! Ctrl+Z Undo shortcut Bork! Redo Bork! Ctrl+Y Redo shortcut Bork! Snap To Bork! Tools Bork! Boolean Operation... Groups|Boolean Operation Bork! Edit Model Meta Data... Groups|Edit Model Meta Data Bork! Transform Model... Groups|Transform Model Bork! Edit Joints... Joints|Edit Joints Bork! Assign Selected to Joint Joints|Assign Selected to Joint Bork! Remove All Influences from Selected Joints|Remove All Influences from Selected Bork! Remove Selected Joint from Influencing Joints|Remove Selected Joint from Influencing Bork! Convert Multiple Influences to Single Joints|Convert Multiple Influences to Single Bork! Select Joint Influences Joints|Select Joint Influences Bork! Select Influenced Vertices Joints|Select Influenced Vertices Bork! Select Influenced Points Joints|Select Influenced Points Bork! Select Unassigned Vertices Joints|Select Unassigned Vertices Bork! Select Unassigned Points Joints|Select Unassigned Points Bork! Start Animation Mode... Animation|Start Animation Mode Bork! Stop Animation Mode Animation|Stop Animation Mode Bork! Animation Sets... Animation|Animation Sets Bork! Export Animation... Animation|Export Animation Bork! Copy Animation Frame Animation|Copy Animation Frame Bork! Paste Animation Frame Animation|Paste Animation Frame Bork! Clear Animation Frame Animation|Clear Animation Frame Bork! Set Rotation Keyframe Animation|Set Rotation Keyframe Bork! Set Translation Keyframe Animation|Set Translation Keyframe Bork! Contents... Help|Contents Bork! License... Help|License Bork! About... Help|About Bork! &File menu bar Bork! &View menu bar Bork! &Tools menu bar Bork! &Primitives menu bar Bork! &Groups menu bar Bork! &Influences menu bar Bork! &Animation menu bar Bork! &Help menu bar Bork! All Supported Formats ( All Borked Formats ( ;; All Files (*) ;; All Bork! (*) All Files (*) All Bork! (*) Save model file as Bork! File exists. Overwrite? Bork! All Supported Formats ( model formats All Borked Formats ( Open model file Bork! Merge models Bork! : Bork! Script %1 complete Bork! Script %1 error %2 Bork! Save first? Bork! Model has been modified Do you want to save before closing? Bork! Unknown response: %1, Canceling close request Bork! Hide Properties View|Hide Properties Bork! Cannot hide with selected projections. Unselect projections now? Bork! Hide projections Bork! Cannot hide with selected joints. Unselect joints now? Bork! Hide bone joints Bork! You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'edit texture coordinates' window Bork! You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'paint texture' window Bork! Undo %1 Bork! Nothing to undo Bork! Redo %1 Bork! Nothing to redo Bork! This model does not have any animations Bork! Set rotation keframe Bork! Set translation keframe Bork! Unknown response: Canceling operation Bork! [unnamed] For filename in title bar (if not set) Bork! Assigning %1 vertices and %2 points to joints Bork! Assign Selected to Joint Bork! You must have at least one bone joint selected. Bork! Remove All Influences from Selected Bork! Remove Joint from Influencing Bork! Convert To Single Influence Bork! Select Unassigned Vertices Bork! Select Unassigned Points Bork! Select Joint Influences Bork! Select Influences Vertices Bork! Select Influenced Points Bork! Auto-Assign Selected... Joints|Auto-Assign Selected Bork! Auto-Assign Selected to Bone Joints Bork! You must have at least one vertex or point selected. Bork! Open... File|Open Bork! Save As... File|Save As Bork! Export... File|Export Bork! Edit Model Meta Data... Model|Edit Model Meta Data Bork! Transform Model... Model|Transform Model Bork! Boolean Operation... Model|Boolean Operation Bork! Set Background Image... Model|Set Background Image Bork! Merge... Model|Merge Bork! Import Animations... Model|Import Animations Bork! Edit Groups... Materials|Edit Groups Bork! Edit Materials... Materials|Edit Materials Bork! Clean Up... Materials|Clean Up Reload Textures Materials|Reload Textures Bork! Edit Projection... Materials|Edit Projection Bork! Edit Texture Coordinates... Materials|Edit Texture Coordinates Bork! Paint Texture... Materials|Paint Texture Bork! Save Animation Images... Animation|Save Animation Images Bork! Copy Selected Keyframes Animation|Copy Animation Frame Bork! Paste Selected Keyframes Animation|Paste Animation Frame Bork! Clear Rotation Keyframe Animation|Clear Rotation Keyframe Clear Translation Keyframe Animation|Clear Translation Keyframe &Model menu bar Bork! &Geometry menu bar Bork! Mate&rials menu bar Bork! All Exportable Formats Bork! All Writable Formats Bork! Export model Bork! All Supported Formats model formats Bork! Merge animations Clear rotation keframe Clear translation keframe All Supported Formats Bork! F1 Help Shortcut Bork! Export Selected... File|Export Selected You must have at least 1 face, joint, or point selected to Export Selected ViewportSettings F1 Help Shortcut Bork! ViewportSettingsBase Viewport Settings Bork! Canvas Grid Bork! Default Grid Unit Bork! 3D Grid Bork! Grid Lines Bork! X/Y Plane Bork! X/Z Plane Bork! Y/Z Plane Bork! Press F1 for help Bork! &Ok Bork! Alt+O Bork! &Cancel Bork! Alt+C Bork! Decimal Grid Binary Grid Fixed Grid mm3d-1.3.15/translations/mm3d_de.ts000066400000000000000000006145271466047437300170770ustar00rootroot00000000000000 AboutWin Maverick Model 3D - About Über Maverick Model 3D AlignWin F1 Help Shortcut Align X Ausrichten X Align Y Ausrichten Y Align Z Ausrichten Z Align Selected operation complete Ausgewählte ausrichten AlignWinBase Align Selection Auswahl ausrichten Align X Ausrichten X Align minimum Ausrichten Minimum Align center Ausrichten Zentrum Align maximum Ausrichten Maximum Align &X Now Richte &X jetzt aus Alt+X Alt+X Align Y Ausrichten Y Align &Y Now Richte &Y jetzt aus Alt+Y Alt+Y Align Z Ausrichten Z Align &Z Now Richte &Z jetzt aus Alt+Z Alt+Z Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen AnimConvertWinBase Convert To Frame In Frame umwandeln Convert Skeletal to Frame: Wandle Skelett in Frame um: example Skeletal Animation Skelett Animation Frame Animation Frame Animation Convert AnimName Animationsname Frame Anim Name: Frame Animationsname: Frame Count Frame Anzahl TORSO_IDLE 1 1 F1 for help F1 für Hilfe Continue Fortsetzen Cancel Abbrechen Cancel All Alles abbrechen AnimConvertWindow F1 Help Shortcut Convert Skeletal to Frame: Wandle Skelett in Frame um: Skeletal Animation Skelett Animation Convert Frame to Frame: Wandle Frame in Frame um: Frame Animation Frame Animation Convert Frame Relative to Frame: Wandle relativen Frame in Frame um: Frame Relative Animation Convert Unknown Type to Frame: Wandle unbekannten Typ in Frame um: Unknown Type Animation AnimExportWinBase Export Animation Animation exportieren Source Quelle Animation Animation Viewport Ansicht Duration Dauer Iterations Schritte Seconds Sekunden Output Ausgabe Directory Verzeichnis Format Format anim_0001.jpg anim_0001.jpg anim_1.jpg anim_1.jpg anim_0001.png anim_0001.png anim_1.png anim_1.png ... ... Frame Rate Frame Rate Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen AnimExportWindow Skeletal - Skeletal Animation prefix Skelett - Frame - Frame Animation prefix Frame - [None] No viewport for animation image export [Leer] Viewport %1 - Ansicht %1 - F1 Help Shortcut Must have more than 0 frames per second Es müssen mehr als 0 Frames pro Sekunde sein Must have more than 0 seconds of animation Es müssen mehr als 0 Sekunden Animation sein Could not write file: Konnte Datei nicht speichern: Output directory does not exist. Ausgabeverzeichnis existiert nicht. AnimSetWinBase Animation Sets Animations Sets &Down &Runter &Up &Hoch &New &Neu &Rename &Umbennen D&elete &Löschen &Copy &Kopieren &Split &Trennen &Join &Zusammenführen &Merge &Vereinen Con&vert To Frame Animation &In Frame Animation umwandeln Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen AnimSetWindow Skeletal Animation Skelett Animation Frame Animation Frame Animation F1 Help Shortcut New Animation Neue Animation New name: Neuer Name: Rename Animation Split at frame Split animation frame window title Trenne einen Frame Split 'Split' refers to splitting an animation into two separate animations Trennen at frame number the frame number where the second (split) animation begins bei Frame Nummer Cannot Split Cannot split animation window title Trennen nicht möglich Must have at least 2 frames to split split animation Es müssen mindestens 2 Frames existieren um zu trennen Cannot merge animation %1 and %2, frame counts differ. Kann Animation %1 und %2 nicht trennen, Frame Anzahl ist unterschiedlich. Can only merge skeletal animations. Es können nur Skelett Animationen zusammengefügt werden. Animation changes operation complete Änderungen an Animation copy kopieren split trennen AnimWidget <New Animation> <Neue Animation> Start animation mode operation complete Starte Animations Modus New Animation operation complete Neue Animation Frame: Frame: Set FPS Frames per second, operation complete Setze FPS Change Frame Count operation complete Ändere Frame Anzahl Clear frame Remove animation data from frame, operation complete Frame zurücksetzen Paste frame paste frame animation position, operation complete Frame einfügen Paste keyframe Paste keyframe animation data complete KeyFrame einfügen Set Looping Change whether animation loops operation complete End animation mode operation complete Beende Animationsmodus Frame: n/a Frame: -kein- Delete Animation? window title Animation löschen? Are you sure you want to delete this animation? Sind Sie sicher, dass sie diese Animation löschen wollen? Delete Animation Delete animation, operation complete Lösche Animation No frame animation data to paste Keine Frame Animationsdaten zum Einfügen No skeletal animation data to paste Keine Skelett Animationsdaten zum Einfügen AnimWidgetBase Animation Animation FPS FPS Play Abspielen Stop Stop Loop Wiederholen Frames Frames X X Delete Animation Lösche Animation AnimWindow Animations Animationen AutoAssignJointWin F1 Help Shortcut AutoAssignJointWinBase Auto-Assign Bone Joints Verknüpfe Knochengelenke automatisch Only assign to selected joints Weise nur den ausgewählten Gelenken zu Single Einzel Multiple Multi Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen BackgroundSelect All Supported Formats ( Alle unterstützten Formate ( Open background image Öffne Hintergrundbild Could not open file Konnte Datei nicht öffnen BackgroundSelectBase None Kein File... Datei... BackgroundWin F1 Help Shortcut Background Image operation complete Hintergrundbild BackgroundWinBase Select Background Image Wähle Hintergrundbild aus Front Vorne Back Hinten Left Links Right Rechts Top Oben Bottom Unten Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen BoolPanel Boolean Operation Boolsche Operation BoolWin Union boolean operation Union Subtraction boolean operation Subtraktion Intersection boolean operation Schnittpunkt You must have at least once face selected Sie müssen mindestens eine Fläche auswählen Object A triangles are still selected Dreiecke von Objekt A sind noch ausgewählt Union With Selected boolean operation Einheit mit Ausgewählten Subtract Selected boolean operation Subtrahiere Ausgewählte Intersect With Selected boolean operation Schnittpunkt mit Ausgewählten Fuse Selected boolean operation Verschweiße Ausgewählte Select faces to set Select faces to set as 'A' Object in boolean operation Wähle Flächen zum Setzten aus BoolWinBase Boolean Operation Boolsche Operation Operation Operation Fuse Verschweiße Union Union Subtraction Subtraktion Intersection Schnittpunkt Set Object A Setze Objekt A Select faces to set Wähle Flächen zum Setzten aus Subtract Selected Subtrahiere Ausgewählte Cal3dPrompt F1 Help Shortcut Cal3dPromptBase Cal3D Filter Options Cal3D Filter Optionen Save Meshes Meshes speichern All meshes in one file Alle Meshes in einer Datei Group meshes by name Gruppiere Meshes nach Name Save Materials Speichere Materialien CRF (Binary) CRF (Binary) XRF (XML Text) XRF (XML Text) Ok Ok Cancel Abbrechen Command Align Selected... Ausgewählte ausrichten... Cap Holes complete Stopfe Löcher vollständig Could not find gap in selected region Konnte keine Lücke in der ausgewählten Region finden Cap Holes Stopfe Löcher Selected primitives copied Ausgewählte Primitives kopiert You must have at least 1 face, joint, or point selected to Copy Sie müssen mindestens eine Fläche, ein Gelenk oder eine Punkt zum Kopieren auswählen Copy Selected to Clipboard Kopiere Auswahl in Zwischenablage Delete Löschen Deleting joints may destroy skeletal animations Do you wish to continue? Wenn Sie diese Gelenke löschen, könnten Skelettanimationen beschädigt werden Wollen sie fortfahren? Primitives deleted Primitives gelöscht Selected primitives duplicated Ausgewählte Primitives dupliziert You must have at least 1 face, joint, or point selected to Duplicate Sie müssen mindestens eine Fläche, ein Gelenk oder einen Punkt zum Duplizieren auswählen Duplicate complete Duplikation komplett Duplicate Duplizieren Edge Divide complete Kanten trennen komplett You must have 2 adjacent vertices selected to Edge Divide Edge Divide Kanten trennen Edge Turn complete Kanten drehen komplett You must have at least 2 adjacent faces to Edge Turn Edge Turn Kanten drehen Extrude... Extrudieren... Flatten Abflachen Flatten X Abflachen X Flatten Y Abflachen Y Flatten Z Abflachen Z Need at least 1 vertex, joint, point, or face selected Sie müssen mindestens eine Fläche, einen Vertex, ein Gelenk oder einen Punkt ausgewählt haben Selected primitives flattened Ausgewählte Primitives abgeflacht Flip Umdrehen Flip X Umdrehen X Flip Y Umdrehen Y Flip Z Umdrehen Z Selected primitives flipped Ausgewählte Primitives umgedreht Hide Verstecken Hide Unselected Verstecke Nicht-Ausgewähltes Hide Selected Verstecke Auswahl Unhide All Verstecktes wieder anzeigen Selected primitives hidden Ausgewählte Primitves versteckt Primitives unhidden Versteckte Primitives wieder angezeigt Unselected primitives hidden Nicht ausgewählte Primitives versteckt Selection inverted Auswahl invertiert Invert Selection Invertiere Auswahl Normals inverted Normale invertiert Invert Normals Invertiere Normale Make Face From Vertices Mache Fläche aus Vertizes Face created Fläche erstellt Must select exactly 3 vertices Es müssen genau 3 Vertizes ausgewählt sein Paste complete Einfügen komplett Nothing to paste Paste failed: %1 %2 Paste from Clipboard Einfügen aus Zwischenablage Rotate Texture Coordinates Rotiere Textur-Koordinaten Face Fläche Group Gruppe Texture coordinates rotated Textur-Koordinaten rotiert Must select faces Flächen müssen ausgewählt sein Select Free Vertices Wähle freie Vertizes aus Free-floating vertices selected Freischwebende Vertizes ausgewählt Simplify Mesh Mesh vereinfachen Snap Vertices Together Führe Vertizes zusammen Snap All Selected Führe alle ausgewählten Vertizes zusammen Snap Nearest Selected Führe nächstliegende Vertizes innerhalb Auswahl zusammen Snap All and Weld Führe alle Vertizes zusammen und verschmelze Snap Nearest and Weld Führe nächstliegende Vertizes zusammen und verschmelze Spherify... In Sphärenform verwandeln... Subdivide complete Unterteilung komplett You must have at least 1 face selected to Subdivide Faces Subdivide Faces Flächen unterteilen You must have 1 or more vertices selected to unweld. Sie müssen 1 oder mehrere Vertizes auswählen um zu trennen. Unweld Vertices Trenne Vertizes You must have 2 or more vertices selected to weld. Sie müssen mindestens 2 oder mehr Vertizes auswählen um zu verschmelzen. Weld Vertices Verschmelze Vertizes Unwelded %1 vertices into %2 vertices Habe %1 Vertizes getrennt in %2 Vertizes Welded %1 vertices into %2 vertices 1% Vertizes zu 2% Vertizes verschmolzen Normals Face Out Normale nach außen ausrichten Meshes Meshes Normals Normale Vertices Vertizes Faces Flächen CommandWidget You are in animation mode, but there are no animations Sie befinden sich im Animationsmodus, aber es gibt keine Animationen ContextGroup <None> <kein> <New> <Neu> New Group Name of new group, window title Neue Gruppe Enter new group name: Gruppenname für neue Gruppe eingeben: Set Group operation complete Setzte Gruppe Unset Group operation complete Gesetze Gruppe aufheben Set Material operation complete Setzte Material Set Projection operation complete Setze Projektion ContextGroupBase Group Gruppe Projection Name Projektionsname ... ... Material Name Material Name Group Material: Gruppen Material: Group Name Gruppen Name Texture Projection Textur Projektion ContextInfluences <None> <kein> Change Joint Assignment operation complete Ändere Gelenk Zuweisung Change Influence Weight operation complete Change Influence Type operation complete <Mixed> multiple types of bone joint influence <Gemischt> Custom bone joint influence Manuell Auto bone joint influence Automatisch Remainder bone joint influence Verbleibender Auto: %1 Auto: %1 Rem: %1 Verbl: %1 ContextInfluencesBase Weight Gewicht Custom Manuell Auto Automatisch Remaining Verbleibend <None> <Kein> Joint Gelenk <Mixed> <Gemischt> ContextName Rename operation complete Umbenennen ContextNameBase Name Name ContextPanel Properties Window title Eigenschaften ContextPosition Set Position operation complete Setze Position ContextPositionBase Position Position Z Z Y Y X X Dimensions ContextProjection Set Projection Type operation complete Setze Projektions Typ ContextProjectionBase Projection Type Projektions Typ Cylinder Zylinder Sphere Sphäre Plane Ebene ... ... ContextRotation Set Rotation operation complete Setze Rotation ContextRotationBase Rotation Rotation Z Z Y Y X X CubeToolWidget Cube Würfel Segment Segment CylinderToolWidget Segments Segmente Sides Seiten Width Breite Scale Skalieren EllipsoidToolWidget Smoothness: Glätte: Faces: Flächen: Sphere Sphäre From Center Checkbox that indicates if ellipsoid is created from center or far corner von Mitte aus ErrorObject Success Erfolgreich Canceled Abgebrochen Operation not supported for this file type Operation wird für diesen Dateityp nicht unterstützt Invalid argument (internal error) Ungültiges Argument (interner Fehler) File does not exist Datei existiert nicht Permission denied Zugriff verweigert Could not open file Konnte Datei nicht öffnen Could not read from file Konnte nicht von Datei lesen File is the wrong type or corrupted Datei ist vom falschen Typ oder beschädigt Unsupported version Nicht unterstützte Version File contains invalid data Datei enthält ungültige Daten Unexpected end of file Unerwartetes Dateiende Unknown error Unbekannter Fehler Could not write file Konnte Datei nicht speichern This operation is not supported Operation wird nicht unterstützt Write not supported, try "Export..." Schreiben wird nicht unterstützt, versuchen sie "Exportieren..." Unrecognized file extension (unknown type) Unbekannte Dateierweiterung (unbekannter Typ) ExtrudeWin F1 Help Shortcut Extrude complete Extrudieren abgeschlossen Extrude operation complete Extrudieren ExtrudeWinBase Extrude Extrudieren Extrude options Extrudieren Optionen X: X: Z: Z: Y: Y: Make Back Faces Erstelle Rückseiten von Flächen E&xtrude &Extrudieren Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Close Schließen GroupCleanBase Clean Up Säuberung Merge identical materials Identische Materialien zusammenführen Remove unused materials Nicht verwendete Materialien entfernen Merge identical groups Identische Gruppen zusammenführen Remove unused groups Nicht verwendete Gruppen entfernen Ok Ok Cancel Abbrechen GroupCleanWin F1 Help Shortcut Group Clean-up operation complete Merged %1 groups, %2 materials; Removed %3 of %4 groups, %5 of %6 materials GroupWinBase Groups Gruppen No group Keine Gruppe New Neu Rename Umbenennen Delete Löschen Select Faces In Group Wähle Flächen in Gruppe aus Unselect Faces In Group Flächen in Gruppe abwählen Faces Flächen Assign As Group Als Gruppe zuweisen Add To Group zu Gruppe hinzufügen Texture Textur No texture Keine Textur Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen GroupWindow F1 Help Shortcut New group window title Neue Gruppe Enter new group name: Gruppenname für neue Gruppe eingeben: Group name must be between 1 and %1 characters Gruppenname muss zwischen 1 und %1 Zeichen lang sein Bad group name window title Ungültiger Gruppenname Cannot change cannot change group name, window title Ändern nicht möglich You cannot change the default group name Sie können den standard Gruppenname nicht ändern Smoothness: Glätte: Max Angle: Maximaler Winkel: Group changes operation complete Gruppen Änderungen HelpWinBase Help Hilfe Contents Inhalt Back Zurück Forward Vorwärts Ok Ok IqePrompt F1 Help Shortcut IqePromptBase IQE Filter Options Save Meshes Meshes speichern Save Points as Bone Joints Save Skeleton Save Animations Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen JointWin F1 Help Shortcut Rename joint window title Gelenk umbenennen Enter new joint name: Neuen Namen für Gelenk eingeben: Joint changes operation complete Gelenk Änderungen JointWinBase Joints Gelenke None Kein Rename Umbenennen Delete Löschen Selection Auswahl Select Joint Vertices Wähle Gelenk Vertizes aus Select Unassigned Vertices Wähle nicht-zugewiesene Vertizes Assign Selected to Joint Weise Auswahl Gelenk zu Add Selected to Joint Füge Auswahl zu Gelenk hinzu F1 for help F1 für Hilfe Ok Ok Cancel Abbrechen KeyConfig V Select Vertices Tool Shortcut F Select Faces Tool Shortcut C Select Connected Mesh Tool Shortcut G Select Groups Tool Shortcut B Select Bone Joints Tool Shortcut T Select Points Tool Shortcut M Move Tool Shortcut R Rotate Tool Shortcut H Hide Unselected Command Shortcut Shift+H Hide Selected Command Shortcut Delete Delete Command Shortcut Ctrl+D Duplicate Command Shortcut Ctrl+C Copy to Clipboard Command Shortcut Ctrl+V Paste from Clipboard Command Shortcut Insert Extrude Command Shortcut Ctrl+W Weld Command Shortcut Ctrl+N File | New Window Shortcut Ctrl+O File | Open Shortcut Ctrl+S File | Save Shortcut Ctrl+Q File | Quit Shortcut Home View | Frame All Shortcut Shift+Home View | Frame Selected Shortcut Ctrl+G Groups | Edit Groups Shortcut Ctrl+M Groups | Edit Materials Shortcut Ctrl+E Groups | Edit Texture Coordinates Shortcut Ctrl+B Joints | Assign Selected Shortcut Shift+U Unhide All Command Shortcut KeyValueWindowBase Edit Meta Data Editiere Meta Daten Name Name Value Wert Ok Ok Cancel Abbrechen LicenseWin GNU General Public License GNU General Public Lizenz LowLevel Cannot delete root joint Kann dieses Wurzelgelenk nicht löschen Cannot add or delete because you have frame animations. Try "Merge..." instead. Hinzufügen oder löschen nicht möglich, da sie Frame Animationen haben. Versuchen Sie "Zusammenfügen..." stattdessen. Success Erfolgreich Canceled Abgebrochen Operation not supported for this file type Operation wird für diesen Dateityp nicht unterstützt Invalid argument (internal error, probably null pointer argument) Ungültiges Argument (interner Fehler, offenbar "null pointer argument") File does not exist Datei existiert nicht Permission denied Zugriff verweigert Could not open file Konnte Datei nicht öffnen Could not read from file Konnte nicht von Datei lesen File is the wrong type or corrupted Datei ist vom falschen Typ oder beschädigt Unsupported version Nicht unterstützte Version File contains invalid data Datei enthält ungültige Daten Unexpected end of file Unerwartetes Dateiende Unknown error Unbekannter Fehler Invalid error code Ungültiger Fehlercode Could not write file Konnte Datei nicht speichern This operation is not supported Operation wird nicht unterstützt MM3D encountered an unexpected data size problem See Help->About to contact the developers MM3D hat ein unerwartetes Datengröße Problem festgestellt Bitte sehen sie unter Hilfe->Über... nach, um die Entwickler zu kontaktieren The model has a texture that's width or height is not a power of two (2, 4, 8, .., 64, 128, 256, ..). Modell hat eine Texturgröße die keine 2er-Potenz ist (128x128, 256x256, ...) Could not load Konnte nicht laden Model contains no skeletal animations Modell enthält keine Skelett Animationen Model skeletons do not match Modell Skelette passen nicht zusammen This looks like a player model. Do you want to load all sections? Dies sieht wie Spieler-Modell aus. Wollen sie alle Sektionen laden? Could not load texture Konnte Textur nicht laden Write not supported, try "Export..." Schreiben wird nicht unterstützt, versuchen sie "Exportieren..." Unrecognized file extension (unknown type) Unbekannte Dateierweiterung (unbekannter Typ) MD2 requires all groups to have the same material. MD2 erfordert, dass alle Gruppen das selbe Material haben. MD2 export requires all faces to be grouped. MD2 Export erfordert, dass alle Flächen einer Gruppe zugeordnet sind. MD3 export requires all faces to be grouped. MD3 Export erfordert, dass alle Flächen einer Gruppe zugeordnet sind. MD3_PATH+filename is to long. MD3_PATH+Dateiname ist zu lang. Too many animation frames for MD3 export. Zu viele Animations Frames für MD3 export. Too many points for MD3 export. Zu viele Punkte für MD3 Export. Too many groups for MD3 export. Zu viele Gruppen für MD3 Export. Too many faces in a single group for MD3 export Zu viele Flächen in einzelner Gruppe für MD3 Export Too many vertices in a single group for MD3 export Zu viele Vertizes in einzelner Gruppe für MD3 export Set meta data for MD3 export Point name is too large for MD3 export. Punkt name ist zu lang für MD3 export. Group name is too large for MD3 export. Gruppen name ist zu lang für MD3 export. Texture filename is too long. Dateiname der Textur ist zu lang. MM3D does not support CAL3D files in XML format MM3D unterstützt keine CAL3D Dateien im XML Format The file does not contain any mesh or animation data Diese Datei enthält keine Mesh- oder Animations- Daten Set meta data for Cal3D export No data marked for saving as IQE. IQE requires all faces to be grouped. IQE requires points to only have one bone influence. Too many vertexes for MS3D export (max 65,536). Too many faces for MS3D export (max 65,536). Too many groups for MS3D export (max 255). Too many materials for MS3D export (max 255). Too many bone joints for MS3D export (max 255). Bone joints must have unique names for MS3D export. Too many vertexes for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Too many faces for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Set meta data for MS3D export Bone joints must have unique names for SMD export. SMD export requires points to only have one bone influence. Set meta data for SMD export Unsupported D3D version. Missing version in D3D model. Invalid D3D directives count. Missing line count in D3D model. Too few tokens on line in D3D model. Found primitive start without primitive end in D3D model. Line primitive type is not supported for D3D model. Triangle strip primitive type is not supported for D3D model. Triangle fan primitive type is not supported for D3D model. Unsupported primitive type in D3D model. Primitive end without start in D3D model. Incomplete triangle list before primitive end in D3D model. Vertex outside of primitive begin/end in D3D model. Primitive start without end in D3D model. D3D requires all groups to have the same material. MapDirectionBase Which direction? Welche Richtung? Set new texture coordinates from which direction? Setzte neue Textur Koordinaten von welcher Richtung aus? Front Vorne Back Hinten Left Links Right Rechts Top Oben Bottom Unten Ok Ok Cancel Abbrechen Md3Prompt F1 Help Shortcut Md3PromptBase MD3 Filter Options Save as a Player Model (head.md3, upper.md3, lower.md3) Save animation.cfg Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen MergeWinBase Merge Model Modelle zusammenfügen Merge location ?Zusammenfügungs Koordinaten Base Point <origin> Rotation Rotation Translation Translation Merge Options Zusammenfügungs Optionen Include textures Texturen einbeziehen Include animations Animationen einbeziehen Animation Options Animations Optionen Append animations Animationen anhängen Merge if possible Zusammenfügen falls möglich Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen MergeWindow F1 Help Shortcut Merge models operation complete Modelle zusammenfügen MetaWindow F1 Help Shortcut Name meta value key name Name Value meta value 'value' Wert Change meta data operation complete Meta Daten ändern MetaWindowBase Name Name Value Wert Model Meta Data Modell Meta Daten New Neu Delete Löschen Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen ModelViewBase ModelView Modell Ansicht Perspective Perspektive Front Vorne Back Hinten Left Links Right Rechts Top Oben Bottom Unten Orthographic Orthographisch ModelViewport Could not load background %1 Konnte Hintergrund nicht laden %1 Use the middle mouse button to drag/pan the viewport Benutzen Sie den mittleren Mausknopf um die Ansicht zu bewegen OpenGL error = Invalid Value OpenGL Fehler = Ungültiger Wert OpenGL error = Invalid Enum OpenGL Fehler = ungültige Enum OpenGL error = Invalid Operation OpenGL Fehler = ungültige Operation OpenGL error = Stack Overflow OpenGL Fehler = Stack Overflow OpenGL error = Stack Underflow OpenGL Fehler = Stack Underflow OpenGL error = Out Of Memory OpenGL Fehler = Out of Memory OpenGL error = Unknown OpenGL Fehler = Unbekannt Ms3dPrompt F1 Help Shortcut Ms3dPromptBase MS3D Filter Options MS3D Filter Optionen Vertex Format Subversion 0 (Single bone joint influence) Subversion 1 (Multiple bone joints influences, weight scale 255) Subversion 2 (Multiple bone joints influences, weight scale 100) Subversion 3 (Multiple bone joints influences, weight scale 100) Subversion Options Subversion Optionen Vertex Extra Vertex Extra Vertex Extra 2 Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen NewAnimBase New Animation Neue Animation &Name &Name Animation Type Animations Typ &Skeletal &Skelet Alt+S Alt+S &Frame &Frame Alt+F Alt+F &Ok &Ok Alt+O Alt+O &Cancel &Abbrechen Alt+C Alt+C ObjPrompt F1 Help Shortcut ObjPromptBase OBJ Filter Options OBJ Filter Optionen &Save normals &Normale speichern &Save Normals Alt+S Alt+S &Normal Decimal Places &?Normale Dezimal Plätze &Texture Decimal Places &?Textur Dezimal Plätze &Vertex Decimal Places &Vertex Dezimal Plätze Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen PaintTextureWin F1 Help Shortcut File name for saved texture? Dateiname für zu speichernde Textur? File exists. Overwrite? Datei existiert bereits. Überschreiben? Could not write file: Konnte Datei nicht schreiben: PaintTextureWinBase Paint Texture Erstelle Texture Polygons: Polygone: Edges Kanten Filled Gefüllte Filled and Edges Gefüllte und Kanten Vertices Vertizes Hidden Versteckt Visible Sichtbar Clear Background Lösche Hintergrund Save Size: Größe beim Speichern: 64 64 128 128 256 256 512 512 1024 1024 2048 2048 x x Save Texture... Textur speichern unter... Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Close Schließen PluginWinBase Plugin Plugin Version Version Description Beschreibung Status Status Plugins Plugins Filename Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok PluginWindow F1 Help Shortcut PointWin F1 Help Shortcut Rename point window title Punkt umbennen Enter new point name: Neuen Namen für Punkt eingeben: Point changes operation complete Änderungen an Punkten PointWinBase Points Punkte Rename Umbenennen Delete Löschen Bone Joint Knochengelenk (none) (kein) F1 for help F1 für Hilfe Ok Ok Cancel Abbrechen PolyToolWidget Poly Type Polygon Typ Strip Triangle strip option Streifen Fan Triangle fan option Fächer ProjToolWidget Type Typ Cylinder Cylinder projection type Zylinder Sphere Sphere projection type Sphäre Plane Plane projection type Ebene ProjectionWin F1 Help Shortcut Set Projection Type operation complete Setze Projektions Typ Set Triangle Projection operation complete Setze Dreiecks Projektion Apply Projection operation complete Projektion anwenden Reset UV Coordinates operation complete UV-Koordinaten zurücksetzen Rename projection window title Projektion umbenennen Enter new point name: Namen für neuen Punkt eingeben: Rename Projection operation complete Projektion umbenennen CTRL+Z Undo shortcut CTRL+Y Redo shortcut ProjectionWinBase Texture Projection Textur Projektion Material Material Test Pattern Test Muster Cylinder Zylinder Sphere Sphäre Plane Ebene Show: Zeige: Type: Typ: Rename Umbenennen Zoom: Zoom: Remove Faces Flächen löschen Add Faces to Projection Füge Flächen zu Projektion hinzu Apply Projection Projektion anwenden Reset UV Range UV-Weite zurücksetzen Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Close Schließen RgbaWinBase RGBA Window RGBA Fenster Light Property Light Eigenschaften Red Rot Green Grün Blue Blau Alpha Alpha Close Schließen RotateToolWidget X X Y Y Z Z ScaleToolWidget Proportion Proportionen Free Free scaling option Frei Keep Aspect 2D 2D scaling aspect option Behalte 2D Aspekt bei Keep Aspect 3D 3D scaling aspect option Behalte 3D Aspekt bei Point Punkt Center Scale from center Mitte Far Corner Scale from far corner SelectFaceToolWidget Include Back-facing Rückseiten mit einbeziehen SmdPrompt F1 Help Shortcut SmdPromptBase SMD Filter Options Model Type Reference Animation Animation Save Points as Bone Joints Vertex Format GoldSrc (Single bone joint influence) Source (Multiple bone joint influences) Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen SpherifyWin Spherify operation complete Sphären-Effekt StatusBar V: Vertices status bar label F: Faces status bar label G: Groups status bar label B: Bone Joints status bar label P: Points status bar label M: Materials status bar label TextWinBase Ok Ok TextureCoord Reset coordinates? window title Koordinaten zurücksetzten? Are you sure you want to reset texture coordinates for this group? Sind Sie sicher, dass sie die Texturkoordinaten für diese Gruppe zurücksetzten wollen? Move texture coordinates Bewege Textur Koordinaten F1 Help Shortcut CTRL+Z Undo shortcut CTRL+Y Redo shortcut Select texture coordinates Wähle Textur Koordinaten aus TextureCoordBase Texture Coordinates Textur Koordinaten Zoom: Zoom: Mouse Tool Maus Werkzeug Select Auswählen Move Bewegen Scale Skalieren Scale Options Skalierungs Optionen Scale from center Von Mitte skalieren Keep aspect ratio Seitenverhältnis beibehalten Map Scheme Map Schema Triangle Dreieck Quad Quadrat Group Gruppe Reset Coordinates Setze Koordinaten zurück Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Close Schließen Rotate Rotieren Lines Linien Black Schwarz Blue Blau Green Grün Cyan Türkis Red Rot Magenta Magenta Yellow Gelb White Weiß Selection Auswahl Rotate CCW Rotiere CCW Rotate CW Rotiere CW V Flip H Flip TextureWindow All Supported Formats ( all texture formats Alle unterstützten Formate ( Open texture image Öffne Textur Could not open file Konnte Datei nicht öffnen Color Material window title Material Farbe Enter new material name: Geben Sie einen neuen Material Namen ein: Rename texture window title Textur umbenennen Enter new texture name: Neuen Textur Namen eingeben: New Material window title Rename material window title Texture changes Textur Änderungen Shininess Glanz Red Rot Change texture... Change material's texture file Textur ändern... Set texture... Add texture file to material Setze Textur... F1 Help Shortcut TextureWindowBase Materials Materialien None Kein New Material... Neues Material... Rename Umbenennen Delete Löschen Wrap X Clamp X Wrap Y Clamp Y Change Texture... Textur ändern... X X Remove texture Textur entfernen Flat Preview Flache Vorschau 3D Preview 3D Vorschau Alpha Alpha Green Grün Blue Blau Red Rot Ambient Diffuse Diffus Specular Emissive Shininess Glanz Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen Tool Attract Far Attracting far selected primitives Attract far complete Attract Near Attracting near selected primitives Attract near complete Move Background Image Hintergrundbild bewegen Moving background image Bewege Hintergrundbild Cannot move background from 3D view Kann Hintergrund nicht von 3D Ansicht aus bewegen Background move complete Hintergrund bewegen abgeschlossen Move background image Hintergrundbild bewegen Scale Background Image Hintergrundbild skalieren Scaling background image Skaliere Hintergrundbild Cannot scale background from 3D view Kann Hintergrundbild nicht aus 3D Ansicht skalieren Background scale complete Hintergrundbild skalieren abgeschlossen Scale background image Skaliere Hintergrundbild Cube created Würfel erstellt Create Cube Erstelle Würfel Cylinder created Zylinder erstellt Create Cylinder Erstelle Würfel Ellipsoid created Ellipsoid erstellt Create Ellipsoid Erstelle Ellipsoid Joint created Gelenk erstellt Root joint created Wurzelgelenk erstellt Create Bone Joint Erstelle Knochengelenk Moving selected primitives Bewege ausgewählte Primitives Move complete Bewegung abgeschlossen Tip: Hold shift to restrict movement to one dimension Tip: Halten Sie [Shift] gedrückt um die Bewegung auf eine Dimension zu beschränken Move Bewegen Point created Punkt erstellt Create Point Erstelle Punkt Create Polygon Erstelle Polygon Projection created Projektion erstellt Create Projection Erstelle Projektion Rectangle created Rechteck erstellt Create Rectangle Erstelle Rechteck Tip: Hold shift to rotate in 15 degree increments Tip: Halten Sie [Shift] gedrückt, um in 15 Grad Schritten zu drehen Rotating selected primitives Rotiere ausgewählte Primitives Setting rotation point Setze Rotationspunkt Rotate complete Rotation komplett Rotate Rotieren Scale Skalieren Scaling selected primitives Skaliere ausgewählte Primitives Scale complete Skalierung komplett Tip: Hold shift to restrict scaling to one dimension Tip: Halten Sie [Shift] gedrückt, um die Skalierung auf eine Dimension zu beschränken Starting selection Beginne Auswahl Selection complete Auswahl komplett Select Bone Joints Wähle Knochengelenke aus Select Connected Mesh Wähle zusammenhängendes Mesh aus Select Faces Wähle Flächen aus Select Groups Wähle Gruppen aus Select Points Wähle Punkte aus Select Projections Wähle Projektionen aus Select Vertices Wähle Vertizes aus Shear Verzerren Starting shear on selected primitives Verzerre ausgewählte Primitives Shear complete Verzerren abgeschlossen Torus created Torus erstellt Create Torus Erstelle Torus Vertex created Vertex erstellt Create Vertex Erstelle Vertex Dragging selected vertex Ziehe ausgewählten Vertex Must a vertex selected Es muss ein Vertex ausgewählt sein Drag complete Ziehen abgeschlossen Drag Vertex on Edge Ziehe Vertex auf Kante Extrude complete Extrudieren abgeschlossen Extrude Extrudieren Must have faces selected to extrude Es müssen Flächen ausgewählt sein, um zu extrudieren Extruding selected faces Extrudiere ausgewählte Flächen Background Image Hintergrundbild Create Other Erstelle Andere Select Auswahl Attract Anziehen TorusToolWidget Segments Segmente Sides Seiten Width Breite Circle Kreis From Center Checkbox that indicates if torus is created from center or from far corner von Mitte aus TransformWindow Matrix Translate Matrix Translation Matrix Rotate Matrix Rotation Matrix Rotate On Axis Matrix Rotation auf Achse Matrix Scale Matrix Skalierung Apply Matrix Matrix anwenden Transform Cannot Be Undone window title Transformierung kann nicht Rückgängig gemacht werden This transformation cannot be undone. Diese Transformation kann nicht rückgängig gemacht werden. Are you sure you wish to continue? Sind Sie sicher, dass Sie fortfahren möchten? Apply Transformation button Transformation anwenden Cancel Transformation button Breche Transformation ab F1 Help Shortcut TransformWindowBase Transform Model Transformiere Modell X X Y Y Z Z Translate Übersetze Euler Angles Eulersche Winkel Rotate Rotieren Quaternion Angle Winkel Axis Achse Scale Skalieren (bottom row is translation) (untere Reihe ist Übersetzung) Apply Matrix Matrix anwenden Matrix Matrix Apply to: Wende an auf: Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Close Schließen Selected (including animations) Ausgewähltem (inklusive Animationen) Entire Model (including animations) Ganzes Modell (inklusive Animationen) ValueWin F1 Help Shortcut ValueWinBase Value Window Werte Fenster Value Wert Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen Ok Ok Cancel Abbrechen ViewWindow Some models are unsaved. Save before exiting? Einige Modelle sind noch nicht gespeichert. Speichern vor Beenden? Press F1 for help using any window Drücken Sie F1 um zur Hilfe für jedes Fenster zu gelangen Properties Eigenschaften New File|New Neu Save File|Save Speichern Run Script... File|Run Script Starte Skript... Recent Scripts File|Recent Script Zuletzt benutzte Skripte Recent Models File|Recent Models Zuletzt benutzte Modelle Plugins... File|Plugins Plugins... Close File|Close Schließen Quit File|Quit Beenden Hide Joints View|Hide Joints Verstecke Gelenke Draw Joint Lines View|Draw Joint Lines Zeichne Gelenk-Linien Draw Joint Bones View|Draw Joint Bones Zeichne Gelenk-Knochen Draw Texture Projections View|Draw Texture Projections Zeichne Textur Projektionen Hide Texture Projections View|Hide Texture Projections Verstecke Textur Projektionen Use Red Error Texture View|Use Red Error Texture Benutze rote Fehler Textur Use Blank Error Texture View|Use Blank Error Texture Benutze leere Fehler Textur Render 3D Lines View|Render 3D Lines Zeichne Linien in 3D Ansicht Hide 3D Lines View|Hide 3D Lines Verstecke Linien in 3D Ansicht Draw Back-facing Triangles View|Draw Back-facing Triangles Zeichne Rückseiten von Dreiecken Hide Back-facing Triangles View|Hide Back-facing Triangles Verstecke Rückseiten von Dreiecken Frame All View|Frame Alle Frames Frame Selected View|Frame Ausgewählte Frames Show Properties View|Show Properties Zeige Eigenschaften Render Options View|Render Options Render Optionen 3D Wireframe View|3D 3D Gitter 3D Flat View|3D 3D Flächen 3D Smooth View|3D 3D Geglättet 3D Texture View|3D 3D Texturiert 3D Alpha Blend View|3D 3D mit Alpha Blending Canvas Wireframe View|Canvas Zeichenfläche Gitter Canvas Flat View|Canvas Zeichenfläche Flächen Canvas Smooth View|Canvas Zeichenfläche Geglättet Canvas Texture View|Canvas Zeichenfläche Texturiert Canvas Alpha Blend View|Canvas Zeichenfläche mit Alpha Blending 1 View View|Viewports 1 Ansicht 1x2 View View|Viewports 1x2 Ansicht 2x1 View View|Viewports 2x1 Ansicht 2x2 View View|Viewports 2x2 Ansichten 2x3 View View|Viewports 2x3 Ansichten 3x2 View View|Viewports 3x2 Ansichten 3x3 View View|Viewports 3x3 Ansichten Viewport Settings... View|Viewport Settings Ansichtsfenster Einstellungen... Grid Tools|Snap to Grid Gitter Vertex Tools|Snap to Vertex Vertex Undo Rückgängig Ctrl+Z Undo shortcut Redo Wiederholen Ctrl+Y Redo shortcut Snap To Schnappe auf Tools Werkzeuge Edit Joints... Joints|Edit Joints Editiere Gelenke... Assign Selected to Joint Joints|Assign Selected to Joint Ausgewähltem Gelenk zuweisen Remove All Influences from Selected Joints|Remove All Influences from Selected Alle Einflüsse von Auswahl entfernen Remove Selected Joint from Influencing Joints|Remove Selected Joint from Influencing ?Entferne Ausgewähltes Gelenk von Anliegenden Convert Multiple Influences to Single Joints|Convert Multiple Influences to Single ?Konvertiere Mehrere Einflüsse auf Einzelnen Select Joint Influences Joints|Select Joint Influences ?Wähle Gelenk Einflüsse Select Influenced Vertices Joints|Select Influenced Vertices Wähle beeinflusste Vertizes Select Influenced Points Joints|Select Influenced Points Wähle beeinflusste Punkte Select Unassigned Vertices Joints|Select Unassigned Vertices Wähle nicht-zugewiesene Vertizes Select Unassigned Points Joints|Select Unassigned Points Wähle nicht-zugewiesene Punkte Start Animation Mode... Animation|Start Animation Mode Starte Animationsmodus... Stop Animation Mode Animation|Stop Animation Mode Beende Animationsmodus Animation Sets... Animation|Animation Sets Animations Sets... Copy Animation Frame Animation|Copy Animation Frame Kopiere Animations Frame Paste Animation Frame Animation|Paste Animation Frame Animations Frame einfügen Clear Animation Frame Animation|Clear Animation Frame ?Animationsframe löschen Set Rotation Keyframe Animation|Set Rotation Keyframe Setze Rotations Keyframe Set Translation Keyframe Animation|Set Translation Keyframe Setze Translations Keyframe Clear Rotation Keyframe Animation|Clear Rotation Keyframe Clear Translation Keyframe Animation|Clear Translation Keyframe Contents... Help|Contents Inhalt... License... Help|License Lizenz... About... Help|About Über... &File menu bar &Datei &View menu bar &Ansicht &Tools menu bar &Werkzeuge &Influences menu bar &Einflüsse &Animation menu bar &Animation &Help menu bar &Hilfe All Files (*) Alle Dateien (*) Save model file as Speichere Modell Datei als Open model file Öffne Modell Datei Merge models Modelle zusammenfügen : : Merge animations Script %1 complete Skript %1 komplett Script %1 error %2 Skript %1 Fehler %2 Save first? Zuerst speichern? Model has been modified Do you want to save before closing? Modell wurde geändert Wollen Sie speichern bevor sie schließen? Unknown response: %1, Canceling close request Unbekannte Antwort: %1, Abbruch der Close-Anweisung Cannot hide with selected projections. Unselect projections now? Verstecken mit den ausgewählten Projektionen nicht möglich. Projektionen de-selektieren? Hide projections Projektionen verstecken Cannot hide with selected joints. Unselect joints now? Verstecken mit den ausgewählten Geleken nicht möglich. Gelenke de-selektieren? Hide bone joints Verstecke Knochengelenke You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'edit texture coordinates' window Sie müssen zuerst Flächen auswählen. Benutzen sie das 'Wähle Flächen' Werkzeug. You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'paint texture' window Sie müssen zuerst Flächen auswählen. Benutzen sie das 'Wähle Flächen' Werkzeug. Undo %1 Ruckgängig %1 Nothing to undo Es gibt nichts rückgängig zu machen Redo %1 Wiederhole %1 Nothing to redo Es gibt nichts zu wiederholen This model does not have any animations Dieses Modell hat keine Animationen Set rotation keframe Setze Rotations Keyframe Set translation keframe Setzte Translations Keyframe Clear rotation keframe Clear translation keframe Unknown response: Canceling operation Unbekannte Anwort: Breche Operation ab [unnamed] For filename in title bar (if not set) [unbenannt] Assigning %1 vertices and %2 points to joints Weise %1 Vertizes und %2 Punkte Gelenken zu Assign Selected to Joint Weise Auswahl Gelenk zu You must have at least one bone joint selected. Sie müssen mindestens ein Knochengelenk ausgewählt haben. Remove All Influences from Selected Entferne alle Einflüsse von Auswahl Remove Joint from Influencing Gelenk von der Beeinflussung entfernen Convert To Single Influence In Einzeleinfluss umwandeln Select Unassigned Vertices Wähle nicht zugewiesene Vertizes Select Unassigned Points Wähle nicht zugewiesene Punkte Select Joint Influences Wähle Gelenk Einflüsse Select Influences Vertices Wähle beeinflusste Vertizes Select Influenced Points Wähle beeinflusste Punkte Auto-Assign Selected... Joints|Auto-Assign Selected Auswahl Automatisch zuweisen... Auto-Assign Selected to Bone Joints Automatisch Auswahl zu Knochengelenken zuweisen You must have at least one vertex or point selected. Sie müssen mindestens einen Vertex oder einen Punkt ausgewählt haben. Open... File|Open Öffnen... Save As... File|Save As Speichern unter... Export... File|Export Exportieren... Edit Model Meta Data... Model|Edit Model Meta Data Editiere Modell Meta Daten... Transform Model... Model|Transform Model Transformiere Modell... Boolean Operation... Model|Boolean Operation Boolsche Operation... Set Background Image... Model|Set Background Image Setze Hintergrundbild... Merge... Model|Merge Vereinen... Import Animations... Model|Import Animations Importiere Animationen... Edit Groups... Materials|Edit Groups Editiere Gruppen... Edit Materials... Materials|Edit Materials Editiere Materialien... Clean Up... Materials|Clean Up Säubern... Reload Textures Materials|Reload Textures Texuren neu laden Edit Projection... Materials|Edit Projection Editiere Projektion... Edit Texture Coordinates... Materials|Edit Texture Coordinates Editiere Textur Koordinaten... Paint Texture... Materials|Paint Texture Erstelle Textur... Save Animation Images... Animation|Save Animation Images Speichere Animationsbilder... Copy Selected Keyframes Animation|Copy Animation Frame Kopiere Ausgewählte Keyframes Paste Selected Keyframes Animation|Paste Animation Frame Füge Ausgewählte Keyframes ein &Model menu bar &Modell &Geometry menu bar &Geometrie Mate&rials menu bar &Materialien All Exportable Formats Alle exportierbaren Formate All Writable Formats Alle schreibbaren Formate Export model Exportiere Modell All Supported Formats model formats Alle unterstützten Formate All Supported Formats Alle unterstützten Formate F1 Help Shortcut Export Selected... File|Export Selected Exportiere Auswahl... You must have at least 1 face, joint, or point selected to Export Selected Sie müssen mindestens eine Fläche, ein Gelenk oder einen Punkt zum Exportieren auswählen ViewportSettings F1 Help Shortcut ViewportSettingsBase Viewport Settings Ansichtsfenster Einstellungen Canvas Grid Zeichenflächen Gitter Default Grid Unit Standard Gitter Einheit 3D Grid 3D Gitter Grid Lines Gitterlinien X/Y Plane X/Y Ebene X/Z Plane X/Z Ebene Y/Z Plane Y/Z Ebene Press F1 for help Drücken Sie F1 um zur Hilfe zu gelangen &Ok &Ok Alt+O Alt+O &Cancel &Abbrechen Alt+C Alt+C Decimal Grid Binary Grid Fixed Grid mm3d-1.3.15/translations/mm3d_fr.ts000066400000000000000000006177221466047437300171160ustar00rootroot00000000000000 AboutWin Maverick Model 3D - About Maverick Model 3D - A Propos AlignWin F1 Help Shortcut Align X Aligner X Align Y Aligner Y Align Z Aligner Z Align Selected operation complete Aligner la selection AlignWinBase Align Selection Aligner la selection Align X Aligner X Align minimum Aligner au minimum Align center Aligner au centre Align maximum Aligner au maximum Align &X Now Aligner &X Maintenant Alt+X Alt+X Align Y Aligner X Align &Y Now Aligner &Y Maintenant Alt+Y Alt+Y Align Z Aligner Z Align &Z Now Aligner &Z Maintenant Alt+Z Alt+Z Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler AnimConvertWinBase Convert To Frame Convertir en pose (Morph Frame) Convert Skeletal to Frame: Convertir Squelette en Pose (Morph frame): example Skeletal Animation Animation par squelette Frame Animation Animation par pose Convert AnimName Nom de l'animation Frame Anim Name: Nom de la pose (Morph Frame) de l'animation: Frame Count Nombre de poses (Morph frames) TORSO_IDLE 1 1 F1 for help Appuyer sur F1 pour l'aide Continue Continuer Cancel Annuler Cancel All Annuler Tout AnimConvertWindow F1 Help Shortcut Convert Skeletal to Frame: Convertir Squelette en Pose (frame): Skeletal Animation Animation par squelette Convert Frame to Frame: Convertir Pose en Pose (frame): Frame Animation Animation par pose Convert Frame Relative to Frame: Convertir Pose Relative en Pose (frame): Frame Relative Animation Convert Unknown Type to Frame: Convertir Type Inconnu en Pose (frame): Unknown Type Animation AnimExportWinBase Export Animation Exporter l'Animation Source Source Animation Animation Viewport Visualisation Duration Durée Iterations Itérations Seconds Secondes Output Sortie Directory Répertoire Format Format anim_0001.jpg anim_0001.jpg anim_1.jpg anim_1.jpg anim_0001.png anim_0001.png anim_1.png anim_1.png ... ... Frame Rate Taux d'images par secondes Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler AnimExportWindow Skeletal - Skeletal Animation prefix Squelette - Frame - Frame Animation prefix Pose (Frame) - [None] No viewport for animation image export [Aucun] Viewport %1 - Visualisation %1- F1 Help Shortcut Must have more than 0 frames per second Il est obligatoire d'avoir plus de 0 images par seconde Must have more than 0 seconds of animation Il est obligatoire d'avoir plus de 0 secondes par animation Could not write file: Impossible d'écrire le fichier: Output directory does not exist. Le répertoire destination n'existe pas. AnimSetWinBase Animation Sets Ensemble d'Animation &Down &Bas &Up &Haut &New &Nouveau &Rename &Renommer D&elete &Effacer &Copy &Copier &Split &Séparer &Join &Joindre &Merge &Fusionner Con&vert To Frame Animation Con&vertir en Animation de pose (Frame) Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler AnimSetWindow Skeletal Animation Animation par squelette Frame Animation Animation par pose F1 Help Shortcut New Animation Nouvelle Animation New name: Nouveau Nom : Rename Animation Split at frame Split animation frame window title Séparer l'animation à cette image Split 'Split' refers to splitting an animation into two separate animations Séparer at frame number the frame number where the second (split) animation begins à l'image numéro Cannot Split Cannot split animation window title Impossible de séparer Must have at least 2 frames to split split animation Il faut avoir 2 images pour découper Cannot merge animation %1 and %2, frame counts differ. Impossible de fusionner l'animation %1 et %2, Le nombre d'images diffère. Can only merge skeletal animations. Ne peut fusionner que les animation par squelette. Animation changes operation complete Changement d'animation copy copier split découper AnimWidget F1 Help Shortcut F1 <New Animation> <Nouvelle Animation> Start animation mode operation complete Entrer en mode Animation New Animation operation complete Nouvelle Animation Frame: Image: Set FPS Frames per second, operation complete Choisir le nombre d'image par secondes Change Frame Count operation complete Changer le nombre d'images Clear frame Remove animation data from frame, operation complete Effacer l'image Paste frame paste frame animation position, operation complete Coller l'image (la pose) Paste keyframe Paste keyframe animation data complete Coller l'image clé (pose clé) Set Looping Change whether animation loops operation complete End animation mode operation complete Sortir du mode d'animation Frame: n/a Image: n/a Delete Animation? window title Effacer l'animation ? Are you sure you want to delete this animation? Etes vous sûre de vouloir effacer cette animation ? Delete Animation Delete animation, operation complete Effacer l'animation No frame animation data to paste Pas de données d'animation de pose (images) No skeletal animation data to paste Pas de données d'animation de squelette AnimWidgetBase Animation Animation FPS Images Par Seconde Play Jouer Stop Stop Loop Boucle Frames Images X X Delete Animation Effacer l'animation AnimWindow Animations Animations AutoAssignJointWin F1 Help Shortcut AutoAssignJointWinBase Auto-Assign Bone Joints Assigner automatiquement les joints os Only assign to selected joints Assigner seulement au joints selectionnés Single Simple Multiple Plusieurs Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler BackgroundSelect All Supported Formats ( Tous les formats supportés ( Open background image Ouvrir l'image de fond All Files (*) Tous les fichiers (*) Could not open file Impossible d'ouvrir le fichier BackgroundSelectBase None Aucun File... Fichier... BackgroundWin F1 Help Shortcut Background Image operation complete Image de fond BackgroundWinBase Select Background Image Selectionner l'image de fond Front Devant Back Derrière Left Gauche Right Droite Top Haut Bottom Bas Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler BoolPanel Boolean Operation Opération Booléene BoolWin Union boolean operation Union Subtraction boolean operation Soustraction Intersection boolean operation Intersection You must have at least once face selected Au moins une face doit être selectionné Object A triangles are still selected Les triangles de l'objet A sont encore selectionnés Union With Selected boolean operation Union avec la selection Subtract Selected boolean operation Soustraire la selection Intersect With Selected boolean operation Intersection avec la selection Fuse Selected boolean operation Fusionner la selection Select faces to set Select faces to set as 'A' Object in boolean operation Selectionner les faces pour les opérations BoolWinBase Boolean Operation Opération booléene Operation Opération Fuse Fusionner Union Union Subtraction Soustraction Intersection Intersection Set Object A Choisir l'objet A Select faces to set Selectionner les faces à fixer Subtract Selected Soustraire la selection Cal3dPrompt F1 Help Shortcut Cal3dPromptBase Cal3D Filter Options Options du filtre Cal3D Save Meshes Sauvegarder les Meshes All meshes in one file Tous les meshes dans un seul fichier Group meshes by name Grouper les meshes par nom Save Materials Sauver les materiaux CRF (Binary) CRF (binaire) XRF (XML Text) XRF (texte xml) Ok Ok Cancel Annuler Command Align Selected... Aligner la selection... Cap Holes complete Remplissage des trous terminés Could not find gap in selected region Impossible de trouver de trous dans la région selectionné Cap Holes Remplir les trous Selected primitives copied Primitives selectionnées copiées You must have at least 1 face, joint, or point selected to Copy Vous devez avoir au moins 1 face, joint ou point selectionné pour le Copier Copy complete Copie terminée Copy Selected to Clipboard Copier la selection dans le presse-papiers Delete Effacer Deleting joints may destroy skeletal animations Do you wish to continue? Effacer les joins peut détruire des animations de squelette Etes vous sûr de vouloir continuer? Primitives deleted Primitives effacées Selected primitives duplicated Selectionner les primitives dupliquées You must have at least 1 face, joint, or point selected to Duplicate Vous devez avoir au moins 1 face, joint, ou point selectionné à dupliquer Duplicate complete Duplication terminée Duplicate Dupliquer Edge Divide complete Division des bords terminée You must have 2 adjacent vertices selected to Edge Divide Vous devez avoir 2 vertex adjacent pour diviser un bord Edge Divide Division de bord Edge Turn complete Tour de bord complet You must have at least 2 adjacent faces to Edge Turn Vous devez avoir 2 vertex adjacent pour tourner un bord Edge Turn Tourner un Bord Extrude... Extrusion... Flatten Aplatir Flatten X Aplatir X Flatten Y Aplatir Y Flatten Z Aplatir Z Need at least 1 vertex, joint, point, or face selected Vous devez avoir au moins 1 face, joint ou point selectionné Selected primitives flattened Primitives selectionnées aplatie Flip Inverser Flip X Inverser X Flip Y Inverser Y Flip Z Inverser Z Selected primitives flipped Primitives selectionnées inversées Hide Cacher Hide Unselected Cacher ce qui n'est pas selectionné Hide Selected Cacher la selection Unhide All Rendre tout visible Selected primitives hidden Selectionner les primitives cachées Primitives unhidden Primitives ré-affichées Unselected primitives hidden Deselectionner les primitives cachées Selection inverted Selection Inversée Invert Selection Inversion de la selection Normals inverted Normals Inversées Invert Normals Inversion des normales Make Face From Vertices Construire un Face depuis les vertex Face created Face Crée Must select exactly 3 vertices Il faut selectionner exactement 3 vertex Paste complete Coller terminé Nothing to paste Paste failed: %1 %2 Paste from Clipboard Coller depuis le presse-papiers Rotate Texture Coordinates Tourner les Coordonnées de texture Face Face Group Groupe Texture coordinates rotated Coordonnées de texture tournées Must select faces Il faut selectionner des faces Select Free Vertices Selectionner les Vertex Libres Free-floating vertices selected Vertex Libres Flottants Selectionnés Simplify Mesh Simplifier le Mesh (Maillage) Snap Vertices Together Coller les Vertex Ensemble Snap All Selected Coller Ensemble la Selection Snap Nearest Selected Coller au plus proche selectionné Snap All and Weld Coller Ensemble et Souder Snap Nearest and Weld Coller au plus proche et Souder Spherify... Arrondir... Subdivide complete Sousdivision terminée You must have at least 1 face selected to Subdivide Faces Subdivide Faces Sousdiviser les Faces You must have 1 or more vertices selected to unweld. Vous devez avoir au moins un vertex selectionné pour Défusionner. Unweld Vertices Défusionner les Vertex You must have 2 or more vertices selected to weld. Vous devez avoir au moins 2 vertex selectionné pour fusionner. Weld Vertices Fusionner les Vertex Unwelded %1 vertices into %2 vertices Défusionner %1 vertex en %2 vertex Welded %1 vertices into %2 vertices Fusionner %1 vertex en %2 vertex Normals Face Out Normales Dirigées vers le Faces Extérieures Meshes Meshes (Maillages) Normals Normales Vertices Vertex Faces Faces CommandWidget You are in animation mode, but there are no animations ContextGroup <None> <aucun> <New> <Nouveau> New Group Name of new group, window title Nouveau groupe Enter new group name: Entrer le nom du nouveau groupe: Set Group operation complete Fixer le Groupe Unset Group operation complete Défaire le Groupe Set Material operation complete Fixer le Materiau Set Projection operation complete Fixer la Projection ContextGroupBase Group Group Projection Name Nom de la Projection ... ... Material Name Nom du Materiau Group Material: Matériau du Group: Group Name Nom du Groupe Texture Projection Projection de Texture ContextInfluences <None> <aucun> Change Joint Assignment operation complete Changer le facteur d'influence Change Influence Weight operation complete Changer le coefficient d'influence Change Influence Type operation complete Changer le type d'influence <Mixed> multiple types of bone joint influence <Mélange> Custom bone joint influence Personnalisé Auto bone joint influence Auto Remainder bone joint influence Rappel Auto: %1 Auto: %1 Rem: %1 Rem: %1 ContextInfluencesBase Weight Poids Custom Personnalisé Auto Auto Remaining Restant <None> <aucun> Joint Joint <Mixed> <Mélange> ContextName Rename operation complete Renommer ContextNameBase Name Nom ContextPanel Properties Window title Propriétés ContextPosition Set Position operation complete Choisir la position ContextPositionBase Position Choisir la position Z Z Y Y X X Dimensions ContextProjection Set Projection Type operation complete Choisir le type de projection ContextProjectionBase Projection Type Type de Projection Cylinder Cylindre Sphere Sphère Plane Plan ... ... ContextRotation Set Rotation operation complete Choisir la rotation ContextRotationBase Rotation Rotation Z Z Y Y X X CubeToolWidget Cube Cube Segment Segment CylinderToolWidget Segments Segments Sides Cotés Width Largeur Scale Mise à l'échelle EllipsoidToolWidget Smoothness: Adoucissemment : Faces: Faces: Sphere Sphère From Center Checkbox that indicates if ellipsoid is created from center or far corner Depuis le Centre ErrorObject Success Succès Canceled Annulé Operation not supported for this file type Opération non supportée pour ce type de fichiers Invalid argument (internal error) Argument Invalide (erreur interne) File does not exist Le Fichier est inexistant Permission denied Permission Refusée Could not open file Impossible d'ouvrir le fichier Could not read from file Impossible de lire depuis le fichier File is the wrong type or corrupted Le fichier est soit corrompu soit du mauvais type Unsupported version Version non supporté File contains invalid data Le fichier contient des données invalides Unexpected end of file Fin de fichier innattendue Unknown error Erreur inconnue Could not write file Impossible d'ecrire le fichier This operation is not supported Cette opération n'est pas supportée Write not supported, try "Export..." Ecriture non supportée, essayer "Exporter..." Unrecognized file extension (unknown type) Extension de fichier inconnue (type inconnu) ExtrudeWin F1 Help Shortcut Extrude complete Extrusion Terminée Extrude operation complete Extrusion ExtrudeWinBase Extrude Extrusion Extrude options Options d'extrusions X: X: Z: Z: Y: Y: Make Back Faces Fabriquer les Faces Arrières E&xtrude E&xtrusion Press F1 for help Appuyer sur F1 pour l'aide Close Fermer GroupCleanBase Clean Up Merge identical materials Remove unused materials Merge identical groups Remove unused groups Ok Ok Cancel Annuler GroupCleanWin F1 Help Shortcut Group Clean-up operation complete Merged %1 groups, %2 materials; Removed %3 of %4 groups, %5 of %6 materials GroupWinBase Groups Goupes No group Pas de groupe New Nouveau Rename Renommer Delete Effacer Select Faces In Group Selectionner les faces dans le Groupe Unselect Faces In Group Déselectionner les faces dans le Groupe Faces Faces Assign As Group Assigner comme Groupe Add To Group Ajouter au Groupe Texture Texture No texture Pas de Texture Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler GroupWindow F1 Help Shortcut New group window title Nouveau groupe Enter new group name: Entrer le nom du nouveau groupe: Group name must be between 1 and %1 characters Le nom de groupe doit être entre 1 et %1 caractères Bad group name window title Mauvais nom de groupe Cannot change cannot change group name, window title Changement impossible You cannot change the default group name Impossible de changer le nom de groupe par défaut Smoothness: Adoucissemment : Max Angle: Angle Maximal : Group changes operation complete Changement de groupes HelpWinBase Help Aide Contents Contenu Back Arrière Forward Avant Ok Ok IqePrompt F1 Help Shortcut IqePromptBase IQE Filter Options Save Meshes Sauvegarder les Meshes Save Points as Bone Joints Save Skeleton Save Animations Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler JointWin F1 Help Shortcut Rename joint window title Renommer le joint Enter new joint name: Entrer un nouveau nom de point : Joint changes operation complete Modification de Joints JointWinBase Joints joints None Aucun Rename Renommer Delete Effacer Selection Selection Select Joint Vertices Selectionner les vertex du joint Select Unassigned Vertices Selectionner les vertex non-assignés Assign Selected to Joint Assigner la selection au joint Add Selected to Joint Ajouter la selection au joint F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler KeyConfig V Select Vertices Tool Shortcut F Select Faces Tool Shortcut C Select Connected Mesh Tool Shortcut G Select Groups Tool Shortcut B Select Bone Joints Tool Shortcut T Select Points Tool Shortcut M Move Tool Shortcut R Rotate Tool Shortcut H Hide Unselected Command Shortcut Shift+H Hide Selected Command Shortcut ? Unhide All Command Shortcut ? Delete Delete Command Shortcut Ctrl+D Duplicate Command Shortcut Ctrl+C Copy to Clipboard Command Shortcut Ctrl+V Paste from Clipboard Command Shortcut Insert Extrude Command Shortcut Ctrl+W Weld Command Shortcut Ctrl+N File | New Window Shortcut Ctrl+O File | Open Shortcut Ctrl+S File | Save Shortcut Ctrl+Q File | Quit Shortcut Home View | Frame All Shortcut Shift+Home View | Frame Selected Shortcut Ctrl+G Groups | Edit Groups Shortcut Ctrl+M Groups | Edit Materials Shortcut Ctrl+E Groups | Edit Texture Coordinates Shortcut Ctrl+B Joints | Assign Selected Shortcut Shift+U Unhide All Command Shortcut KeyValueWindowBase Edit Meta Data Editer les meta-données Name Nom Value Valeur Ok Ok Cancel Annuler LicenseWin GNU General Public License Licence Publique Générale GNU LowLevel Cannot delete root joint Impossible d'effacer le joint racine Cannot add or delete because you have frame animations. Try "Merge..." instead. Impossible d'ajouter ou d'effacer puisque vous avez des Animations de Pose. Essayer "Fusionner..." à la place. Success Succès Canceled Annulé Operation not supported for this file type Opération non supportée pour ce type de fichiers Invalid argument (internal error, probably null pointer argument) Paramètre Invalide (Erreur interne, probablement un pointeur nulle) File does not exist Le Fichier est inexistant Permission denied Permission Refusée Could not open file Impossible d'ouvrir le fichier Could not read from file Impossible de lire depuis le fichier File is the wrong type or corrupted Le fichier est soit corrompu soit du mauvais type Unsupported version Version non supporté File contains invalid data Le fichier contient des données invalides Unexpected end of file Fin de fichier innattendue Unknown error Erreur inconnue Invalid error code Code d'erreur Invalide Could not write file Impossible d'ecrire le fichier This operation is not supported Cette opération n'est pas supportée MM3D encountered an unexpected data size problem See Help->About to contact the developers MM3D a rencontré un problème de taille de données inattendue Voir "Aide->A Propos" pour contacter les développeurs The model has a texture that's width or height is not a power of two (2, 4, 8, .., 64, 128, 256, ..). Le modèles a une texture qui n'est pas une puissance de 2 Could not load Impossible de charger Model contains no skeletal animations Le modèle ne contient pas de données d'animation de squelette Model skeletons do not match Les squelettes des modèle ne correspondent pas This looks like a player model. Do you want to load all sections? Cela ressemble à un modèle de joueur Voulez-vous charger toutes les sections ? Could not load texture Impossible de charger la texture Write not supported, try "Export..." Ecriture non supportée, essayer "Exporter..." Unrecognized file extension (unknown type) Extension de fichier inconnue (type inconnu) MM3D does not support CAL3D files in XML format The file does not contain any mesh or animation data Set meta data for Cal3D export MD2 requires all groups to have the same material. MD2 export requires all faces to be grouped. MD3 export requires all faces to be grouped. MD3_PATH+filename is to long. Set meta data for MD3 export Point name is too large for MD3 export. Group name is too large for MD3 export. Texture filename is too long. No data marked for saving as IQE. IQE requires all faces to be grouped. IQE requires points to only have one bone influence. Too many vertexes for MS3D export (max 65,536). Too many faces for MS3D export (max 65,536). Too many groups for MS3D export (max 255). Too many materials for MS3D export (max 255). Too many bone joints for MS3D export (max 255). Bone joints must have unique names for MS3D export. Too many vertexes for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Too many faces for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Set meta data for MS3D export Bone joints must have unique names for SMD export. SMD export requires points to only have one bone influence. Set meta data for SMD export Unsupported D3D version. Missing version in D3D model. Invalid D3D directives count. Missing line count in D3D model. Too few tokens on line in D3D model. Found primitive start without primitive end in D3D model. Line primitive type is not supported for D3D model. Triangle strip primitive type is not supported for D3D model. Triangle fan primitive type is not supported for D3D model. Unsupported primitive type in D3D model. Primitive end without start in D3D model. Incomplete triangle list before primitive end in D3D model. Vertex outside of primitive begin/end in D3D model. Primitive start without end in D3D model. D3D requires all groups to have the same material. MapDirectionBase Which direction? Quelle direction ? Set new texture coordinates from which direction? Quelle direction pour le nouveau jeu de coordonnées de texture ? Front Devant Back Derrière Left Gauche Right Droite Top Haut Bottom Bas Ok Ok Cancel Annuler Md3Prompt F1 Help Shortcut F1 Md3PromptBase MD3 Filter Options Save as a Player Model (head.md3, upper.md3, lower.md3) Save animation.cfg Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler MergeWinBase Merge Model Fusionner le modèle Merge location Fusionner les lieux Base Point <origin> Rotation Rotation Translation Translation Merge Options Options de Fusion Include textures Inclure les textures Include animations Inclure les animations Animation Options Options d'Animation Append animations Ajouter les Animations Merge if possible Fusionner si possible Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler MergeWindow F1 Help Shortcut Merge models operation complete Fusionner les modèles Opération Terminée MetaWindow F1 Help Shortcut Name meta value key name Nom Value meta value 'value' Valeur Change meta data operation complete Changer les metadata MetaWindowBase Name Nom Value Valeur Model Meta Data Meta Données du Modèle New Nouveau Delete Effacer Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler ModelViewBase ModelView Vue du Modèle Perspective Perspective Front Devant Back Derrière Left Gauche Right Droite Top Haut Bottom Bas Orthographic Orthographique ModelViewport Could not load background %1 Impossible de charger l'image de fond %1 Use the middle mouse button to drag/pan the viewport Utiliser le boutoun du milieu de la souris pour faire glisser la visualisation OpenGL error = Invalid Value Erreur OpenGL = Valeur Invalide OpenGL error = Invalid Enum Erreur OpenGL = Enumération Invalide OpenGL error = Invalid Operation Erreur OpenGL = Opération Invalide OpenGL error = Stack Overflow Erreur OpenGL = Dépassement de Tas OpenGL error = Stack Underflow Erreur OpenGL = Tas Sous Exploité OpenGL error = Out Of Memory Erreur OpenGL = Plus de mémoire OpenGL error = Unknown Erreur OpenGL = Inconnue Ms3dPrompt F1 Help Shortcut Ms3dPromptBase MS3D Filter Options Options des filtres MS3D Vertex Format Subversion 0 (Single bone joint influence) Subversion 1 (Multiple bone joints influences, weight scale 255) Subversion 2 (Multiple bone joints influences, weight scale 100) Subversion 3 (Multiple bone joints influences, weight scale 100) Subversion Options Options Subversion Vertex Extra Extra Vertex Vertex Extra 2 Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler NewAnimBase New Animation Nouvelle Animation &Name &Nom Animation Type Type d'animation &Skeletal &Squelette Alt+S Alt+S &Frame &Pose Alt+F Atl+F &Ok &Ok Alt+O Alt+O &Cancel &Annuler Alt+C Alt+C ObjPrompt F1 Help Shortcut ObjPromptBase OBJ Filter Options Options du filtre OBJ &Save normals &Sauver les normales &Save Normals Alt+S Alt+S &Normal Decimal Places Position Décimale de la &Normale &Texture Decimal Places Position Décimale de la &Texture &Vertex Decimal Places Position Décimale des &Vertex Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler PaintTextureWin F1 Help Shortcut File name for saved texture? Nom de fichier pour les textures sauvegardées? File exists. Overwrite? Le Fichier existe. Ecraser ? Could not write file: Impossible d'écrire le fichier: PaintTextureWinBase Paint Texture Peindre la Texture Polygons: Polygones: Edges Bords Filled Intérieur Filled and Edges Interieurs et Bords Vertices Vertex Hidden Caché Visible Visible Clear Background Effacer l'image de fond Save Size: Sauvegarder la Dimension: 64 64 128 128 256 256 512 512 1024 1024 2048 2048 x x Save Texture... Sauvegarder la texture... Press F1 for help Appuyer sur F1 pour l'aide Close Fermer PluginWinBase Plugin Plugins (greffons) Version Version Description Description Status Statut Plugins Plugins (greffons) Filename Press F1 for help Appuyer sur F1 pour l'aide Ok Ok PluginWindow F1 Help Shortcut PointWin F1 Help Shortcut Rename point window title Renommer le point Enter new point name: Entrer un nouveau nom de point : Point changes operation complete Modification de Point PointWinBase Points Points Rename Renommer Delete Effacer Bone Joint Joints d'Os (none) (Aucun) F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler PolyToolWidget Fan Fan Poly Type Strip Triangle strip option Fan Triangle fan option Fan ProjToolWidget Type Type Cylinder Cylinder projection type Cylindre Sphere Sphere projection type Sphère Plane Plane projection type Plan ProjectionWin F1 Help Shortcut Set Projection Type operation complete Choisir le type de projection Set Triangle Projection operation complete Choisir la projection des triangles Apply Projection operation complete Appliquer la projection Reset UV Coordinates operation complete Réinitialisation des coordonnées UV Rename projection window title Renommer la projection Enter new point name: Entrer un nouveau nom de point : Rename Projection operation complete Renommer la projection CTRL+Z Undo shortcut CTRL+Y Redo shortcut ProjectionWinBase Texture Projection Projection de Texture Material Matériau Test Pattern Motif de Test Cylinder Cylindre Sphere Sphère Plane Plan Show: Montrer: Type: Type: Rename Renommer Zoom: Zoom: Remove Faces Enlever les Faces Add Faces to Projection Ajouter les Faces à la Projection Apply Projection Appliquer la projection Reset UV Range Remettre à zéro l'intevalle des UV Press F1 for help Appuyer sur F1 pour l'aide Close Fermer RgbaWinBase RGBA Window Fenêtre RGBA Light Property Propriété de la Lumière Red Rouge Green Vert Blue Bleu Alpha Alpha Close Fermer RotateToolWidget X X Y Z ScaleToolWidget Proportion Proportion Free Free scaling option Libre Keep Aspect 2D 2D scaling aspect option Garder l'aspect 2D Keep Aspect 3D 3D scaling aspect option Garder l'aspect 3D Point Point Center Scale from center Center Far Corner Scale from far corner Coin éloigné SelectFaceToolWidget Include Back-facing Inclure les faces arrières SmdPrompt F1 Help Shortcut F1 SmdPromptBase SMD Filter Options Model Type Reference Animation Animation Save Points as Bone Joints Vertex Format GoldSrc (Single bone joint influence) Source (Multiple bone joint influences) Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler SpherifyWin Spherify operation complete Arrondir StatusBar V: Vertices status bar label V: F: Faces status bar label F: G: Groups status bar label G: B: Bone Joints status bar label J: P: Points status bar label P: M: Materials status bar label M: TextWinBase Ok Ok TextureCoord Reset coordinates? window title Remettre les coordonnées à zéro ? Are you sure you want to reset texture coordinates for this group? Voulez-vous remettre les coordonnées à zéro pour ce group ? Move texture coordinates Bouger les Coordonnées de texture F1 Help Shortcut CTRL+Z Undo shortcut CTRL+Y Redo shortcut Select texture coordinates TextureCoordBase Texture Coordinates Coordonnées de texture Zoom: Zoom: Mouse Tool Outil de Pointage Select Selectionner Move Bouger Scale Mise à l'échelle Scale Options Options d'échelle Scale from center Mise à l'échelle depuis le centre Keep aspect ratio Garder le ratio de l'aspect Map Scheme Type de projection Triangle Triangle Quad Quad Group Groupe Reset Coordinates Remettre les coordonnées à zéro Press F1 for help Appuyer sur F1 pour l'aide Close Fermer Rotate Rotation Lines Black Blue Bleu Green Vert Cyan Red Rouge Magenta Yellow White Selection Selection Rotate CCW Rotate CW V Flip H Flip TextureWindow All Supported Formats ( all texture formats Tous les formats supportés ( Open texture image Ouvrire l'image de la texture All Files (*) Tous les fichiers (*) Could not open file Impossible d'ouvrir le fichier Color Material window title Matériau de couleur Enter new material name: Entrer un nouveau nom de matériau : Rename texture window title Renommer la texture Enter new texture name: Entrer le nouveau nom de la texture: New Material window title Rename material window title Texture changes Changements de texture Shininess Brillance Red Rouge Change texture... Change material's texture file Changer de texture... Set texture... Add texture file to material Choisir la texture... F1 Help Shortcut TextureWindowBase Materials Matériaux None Aucun New Material... Nouveau Materiau... Rename Renommer Delete Effacer Wrap X Allonger X Clamp X Borner X Wrap Y Allonger Y Clamp Y Borner Y Change Texture... Changer de Texture... X X Remove texture Enlever la texture Flat Preview Prévisualisation Plate 3D Preview Prévisualisation 3D Alpha Alpha Green Vert Blue Bleu Red Rouge Ambient Ambiant Diffuse Diffuse Specular Speculaire Emissive Emissive Shininess Brillance Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler Tool Attract Far Attraction lointaine Attracting far selected primitives Attire les primitives selectionner de loin Attract far complete Attraction lointaint complète Attract Near Attraction rapprochée Attracting near selected primitives Attraction rapprochée des primitives selectionnées Attract near complete Attraction rapprochée terminées Move Background Image Déplacer l'Image de fond Moving background image Déplace L'image de fond Cannot move background from 3D view Impossible de Déplacer l'image de fond depuis la vue 3D Background move complete Déplacement de l'image de fond terminée Move background image Déplacer l'Image de fond Scale Background Image Mettre à l'echelle l'image de fond Scaling background image Mise à l'echelle l'image de fond Cannot scale background from 3D view Impossible de Mettre à l'Echelle l'image de fond depuis la vue 3D Background scale complete Mise à l'echelle de l'image de fond terminée Scale background image Mettre à l'echelle l'image de fond Cube created Cube crée Create Cube Créer le cube Cylinder created Cylindre crée Create Cylinder Créer le Cylindre Ellipsoid created Ellipsoid crée Create Ellipsoid Créer l'Ellipsoid Joint created Joint crée Root joint created Joint Racine crée Create Bone Joint Créer le Joint d'os Moving selected primitives Bouger les primitives selectionnées Move complete Mouvement Complet Tip: Hold shift to restrict movement to one dimension Astuces: Appuyer sur shift pour restreindre le mouvement à une dimension Move Bouger Point created Point crée Create Point Créer le Point Create Polygon Créer le Polygon Projection created Projection crée Create Projection Créer la Projection Rectangle created Rectange Crée Create Rectangle Créer le Rectange Tip: Hold shift to rotate in 15 degree increments Astuces: Appuyer sur shift pour tourner par incréments de 15 degrés Rotating selected primitives Tourner les primitives selectionnées Setting rotation point Fixe le point de Rotation Rotate complete Rotation Terminée Rotate Tourner Scale Mise à l'échelle Scaling selected primitives Mettre à l'echelle les primitives selectionnées Scale complete Mise à l'echelle terminée Tip: Hold shift to restrict scaling to one dimension Astuces: Appuyer sur shift pour restreindre la mise à l'echelle à une dimension Starting selection Commencement de la selection Selection complete Selection terminée Select Bone Joints Selectionner les Joints d'Os Select Connected Mesh Selectionner les Mesh (maillage) Connectés Select Faces Selectionner les Faces Select Groups Selectionner les Groupes Select Points Selectionner les Points Select Projections Selectionner les Projections Select Vertices Selectionner les Vertex Shear Décaler Starting shear on selected primitives Commencer le décalage sur les Primitives selectionnées Shear complete Décalage Terminé Torus created Torus crée Create Torus Créer le Torus Vertex created Vertex Créer Create Vertex Créer le Vertex Dragging selected vertex Glisser le vertex selectionner Must a vertex selected Un vertex doit être selectionner Drag complete Glisser terminé Drag Vertex on Edge Glisser un Vertex sur un Bord Extrude complete Extrusion Terminée Extrude Extrusion Must have faces selected to extrude Des faces doivent être selectionné pour l'extrusion Extruding selected faces Extrusion des facse selectionnées Background Image Image de fond* Create Other Créer Autre Select Selectionner Attract Attraction TorusToolWidget Segments Segments Sides Cotés Width Largeur Circle Cercle From Center Checkbox that indicates if torus is created from center or from far corner Depuis le Centre TransformWindow Matrix Translate Translation par matrice Matrix Rotate Rotation par matrice Matrix Rotate On Axis Rotation par matrice sur un axe Matrix Scale Agrandissement par matrice Apply Matrix Appliquer la matrice Transform Cannot Be Undone window title La transformation ne peut être annulée This transformation cannot be undone. Cette transformation ne peut être annulée. Are you sure you wish to continue? Etes vous sûr de vouloir continuer ? Apply Transformation button Appliquer la Transformation bouton Cancel Transformation button Annuler la transformation F1 Help Shortcut TransformWindowBase Transform Model Transformer le modèle X X Y 1 Z 1 Translate Translation Euler Angles Angles d'Euleur Rotate Rotation Quaternion Quaternion Angle Angle Axis Axe Scale Mise à l'échelle (bottom row is translation) (La rangée du bas est la translation) Apply Matrix Applique la Matrice Matrix Matrice Apply to: Appliquer à : Entire Model and Animations Tout le Modèle et Animations Press F1 for help Appuyer sur F1 pour l'aide Close Fermer Selected (including animations) Entire Model (including animations) ValueWin F1 Help Shortcut ValueWinBase Value Window Fenêtre de valeur Value Valeur Press F1 for help Appuyer sur F1 pour l'aide Ok Ok Cancel Annuler ViewWindow Some models are unsaved. Save before exiting? Certains modèles n'ont pas été sauvés. Sauver avant de quitter ? Press F1 for help using any window Appuyer sur F1 pour l'aide quelque soit la fenêtre Animations Animations Properties Propriétés New File|New Nouveau Save File|Save Sauvegarder Run Script... File|Run Script Lancer le Script... Recent Scripts File|Recent Script Scripts Recents Recent Models File|Recent Models Modèles Recents Plugins... File|Plugins Plugins (greffons)... Close File|Close Fermer Quit File|Quit Quitter Hide Joints View|Hide Joints Cacher les Joints Draw Joint Lines View|Draw Joint Lines Dessiner les lignes dse joints Draw Joint Bones View|Draw Joint Bones Dessiner les Os des Joints Draw Texture Projections View|Draw Texture Projections Dessiner les Projections de Texture Hide Texture Projections View|Hide Texture Projections Cacher les Projections de Textures Use Red Error Texture View|Use Red Error Texture Utiliser la Texture Erreur Rouge Use Blank Error Texture View|Use Blank Error Texture Utiliser la Texture Erreur Vide Render 3D Lines View|Render 3D Lines Faire le Rendu des lignes 3D Hide 3D Lines View|Hide 3D Lines Cacher les lignes 3D Draw Back-facing Triangles View|Draw Back-facing Triangles Dessiner les Triangles Arrières (BackFacing) Hide Back-facing Triangles View|Hide Back-facing Triangles Cacher les Triangles Arrières (BackFacing) Frame All View|Frame Toutes les Images Frame Selected View|Frame Image Selectionnée Show Properties View|Show Properties Afficher les Propriétés Render Options View|Render Options Options de Rendu 3D Wireframe View|3D Fil de Fer 3D 3D Flat View|3D 3D Platte 3D Smooth View|3D 3D Adoucie 3D Texture View|3D 3D Texturée 3D Alpha Blend View|3D 3D Composition Alpha Canvas Wireframe View|Canvas Zone de Dessin Fil de Fer Canvas Flat View|Canvas Zone de dessin Platte Canvas Smooth View|Canvas Zone de dessin Adoucie Canvas Texture View|Canvas Zone de dessin Texturée Canvas Alpha Blend View|Canvas Zone de dessin avec Composition de l'alpha 1 View View|Viewports 1 Vue 1x2 View View|Viewports 1x2 Vue 2x1 View View|Viewports 2x1 Vue 2x2 View View|Viewports 2x2 Vue 2x3 View View|Viewports 2x3 Vue 3x2 View View|Viewports 3x2 Vue 3x3 View View|Viewports 3x3 Vue Viewport Settings... View|Viewport Settings Configuration de la Visualisation... Grid Tools|Snap to Grid Grille Vertex Tools|Snap to Vertex Vertex Undo Défaire Ctrl+Z Undo shortcut Redo Refaire Ctrl+Y Redo shortcut Snap To Caler à Tools Outils Edit Joints... Joints|Edit Joints Editer les Joints... Assign Selected to Joint Joints|Assign Selected to Joint Assigner la Selection au Joint Remove All Influences from Selected Joints|Remove All Influences from Selected Enlever toutes les influences de la Selection Remove Selected Joint from Influencing Joints|Remove Selected Joint from Influencing Enlever le joint selectionné de l'influence Convert Multiple Influences to Single Joints|Convert Multiple Influences to Single Convertir de multiples influences en une Seule Select Joint Influences Joints|Select Joint Influences Selectionner les Influences du Joint Select Influenced Vertices Joints|Select Influenced Vertices Selectionner les vertex influencés Select Influenced Points Joints|Select Influenced Points Selectionner les Points influencés Select Unassigned Vertices Joints|Select Unassigned Vertices Selectionner les Vertex Non-assignés Select Unassigned Points Joints|Select Unassigned Points Selectionner les Points non-assignés Start Animation Mode... Animation|Start Animation Mode Entrer en Mode Animation... Stop Animation Mode Animation|Stop Animation Mode Quitter le Mode Animation Animation Sets... Animation|Animation Sets Ensemble d'Animation... Copy Animation Frame Animation|Copy Animation Frame Copier La pose (frame) d'animation Paste Animation Frame Animation|Paste Animation Frame Copier la Pose (Frame) d'Animation Clear Animation Frame Animation|Clear Animation Frame Effacer La Pose (Frame) d'animation Set Rotation Keyframe Animation|Set Rotation Keyframe Fixer l'image clée de Rotation Set Translation Keyframe Animation|Set Translation Keyframe Fixer l'image clée de Translation Clear Rotation Keyframe Animation|Clear Rotation Keyframe Clear Translation Keyframe Animation|Clear Translation Keyframe Contents... Help|Contents Contenu... License... Help|License Licence... About... Help|About A propos... &File menu bar &Fichier &View menu bar &Vue &Tools menu bar &Outils &Influences menu bar &Influences &Animation menu bar &Animation &Help menu bar &Aide All Files (*) Tous les Fichiers (*) Save model file as Sauver le ficher sous File exists. Overwrite? Le Fichier existe. Ecraser ? Open model file Ouvrir le fichier modèle Merge models Fusionner les modèles : : Script %1 complete Script %1 terminée Script %1 error %2 Script %1 erreur %2 Save first? Sauvegarder avant ? Model has been modified Do you want to save before closing? Le modèle a été modifié Voulez vous sauver avant de fermer ? Unknown response: %1, Canceling close request Réponse inconnue : %1, Annulation de la requête de fermeture Hide Properties View|Hide Properties Cacher les Propriétés Cannot hide with selected projections. Unselect projections now? Impossible de cacher avec les projections selectionnées. Deselectionner les projections maintenant ? Hide projections Cacher les projections Cannot hide with selected joints. Unselect joints now? Impossible de cacher avec les joints selectionnés. Deselectionner les joints maintenant ? Hide bone joints Cacher les Joints d'Os You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'edit texture coordinates' window Vous devez selectionner les faces avant. Utiliser l'outil de "Selection des Faces". You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'paint texture' window Vous devez selectionner les faces avant. Utiliser l'outil de "Selection des Faces". Undo %1 Défaire %1 Nothing to undo Rien à défaire Redo %1 Refaire %1 Nothing to redo Rien à refaire This model does not have any animations Ce modèle n'a aucune animation Set rotation keframe Fixer l'image clée de rotation Set translation keframe Fixe l'image clée de translation Unknown response: Canceling operation Réponses inconnues : Annuler l'opération [unnamed] For filename in title bar (if not set) [sans nom] Assigning %1 vertices and %2 points to joints Assigner %1 vertex and %2 points au joints Assign Selected to Joint Assigner la selection au joint You must have at least one bone joint selected. Vous devez avoir exactement 1 joint d'os selectionné. Remove All Influences from Selected Enlever toutes les influences de la Selection Remove Joint from Influencing Enlever le joint selectionné de l'influence Convert To Single Influence Convertir de multiples influences en une Seule Select Unassigned Vertices Selectionner les vertex non-assignés Select Unassigned Points Selectionner les Points non-assignés Select Joint Influences Selectionner les Influences du Joint Select Influences Vertices Selectionner les vertex influencés Select Influenced Points Selectionner les Points influencés Auto-Assign Selected... Joints|Auto-Assign Selected Assigner automatiquement la selection... Auto-Assign Selected to Bone Joints Assigner seulement au joints d'os You must have at least one vertex or point selected. Vous devez avoir au moins un vertex ou point selectionné. Open... File|Open Ouvrir... Save As... File|Save As Sauver sous... Export... File|Export Exporter... Edit Model Meta Data... Model|Edit Model Meta Data Editer les meta-données du modèle... Transform Model... Model|Transform Model Transformer le modèle... Boolean Operation... Model|Boolean Operation Opération Booléene... Set Background Image... Model|Set Background Image Fixer l'image de fond... Merge... Model|Merge Fusionner... Import Animations... Model|Import Animations Importer les Animations... Edit Groups... Materials|Edit Groups Editer les Groupes... Edit Materials... Materials|Edit Materials Editer les Matérieaux... Clean Up... Materials|Clean Up Reload Textures Materials|Reload Textures Recharger les Textures Edit Projection... Materials|Edit Projection Editer la Projection... Edit Texture Coordinates... Materials|Edit Texture Coordinates Editer les Coordonnées de texture... Paint Texture... Materials|Paint Texture Peindre la Texture... Save Animation Images... Animation|Save Animation Images Sauver les images des animations... Copy Selected Keyframes Animation|Copy Animation Frame Copier la selection d'images clées Paste Selected Keyframes Animation|Paste Animation Frame Coller la selection d'images clées &Model menu bar &Modèle &Geometry menu bar &Géometrie Mate&rials menu bar &Matériaux All Exportable Formats Tous les formats exportables All Writable Formats Tous les formats inscriptibles Export model Exporter le modèle All Supported Formats model formats Tous les formats supportés Merge animations Clear rotation keframe Clear translation keframe All Supported Formats Tous les formats supportés F1 Help Shortcut Export Selected... File|Export Selected You must have at least 1 face, joint, or point selected to Export Selected ViewportSettings F1 Help Shortcut ViewportSettingsBase Viewport Settings Configuration de la visualisation Canvas Grid Grille de la zone de dessin Default Grid Unit Unité de la grille de défaut 3D Grid Grille 3D Grid Lines Lignes de la Grille X/Y Plane Plan X/Y X/Z Plane Plan X/Z Y/Z Plane Plan Y/Z Press F1 for help Appuyer sur F1 pour l'aide &Ok &Ok Alt+O Alt+O &Cancel &Annuler Alt+C Alt+C Decimal Grid Binary Grid Fixed Grid mm3d-1.3.15/translations/mm3d_ref.ts000066400000000000000000006042401466047437300172520ustar00rootroot00000000000000 AboutWin Maverick Model 3D - About AlignWin F1 Help Shortcut Align X Align Y Align Z Align Selected operation complete AlignWinBase Align Selection Align X Align minimum Align center Align maximum Align &X Now Alt+X Align Y Align &Y Now Alt+Y Align Z Align &Z Now Alt+Z Press F1 for help Ok Cancel AnimConvertWinBase Convert To Frame Convert Skeletal to Frame: example Skeletal Animation Frame Animation Convert Frame Count TORSO_IDLE 1 F1 for help Cancel AnimConvertWindow F1 Help Shortcut Convert Skeletal to Frame: Skeletal Animation Convert Frame to Frame: Frame Animation Convert Frame Relative to Frame: Frame Relative Animation Convert Unknown Type to Frame: Unknown Type Animation AnimExportWinBase Export Animation Source Animation Viewport Duration Iterations Seconds Output Directory Format anim_0001.jpg anim_1.jpg anim_0001.png anim_1.png ... Frame Rate Press F1 for help Ok Cancel AnimExportWindow Skeletal - Skeletal Animation prefix Frame - Frame Animation prefix [None] No viewport for animation image export Viewport %1 - F1 Help Shortcut Must have more than 0 frames per second Must have more than 0 seconds of animation Could not write file: Output directory does not exist. AnimSetWinBase Animation Sets &Down &Up &New &Rename D&elete &Copy &Split &Join &Merge Con&vert To Frame Animation Press F1 for help Ok Cancel AnimSetWindow Skeletal Animation Frame Animation F1 Help Shortcut New Animation New name: Rename Animation Split at frame Split animation frame window title Split 'Split' refers to splitting an animation into two separate animations at frame number the frame number where the second (split) animation begins Cannot Split Cannot split animation window title Must have at least 2 frames to split split animation Cannot merge animation %1 and %2, frame counts differ. Can only merge skeletal animations. Animation changes operation complete copy split AnimWidget <New Animation> Start animation mode operation complete New Animation operation complete Frame: Set FPS Frames per second, operation complete Change Frame Count operation complete Clear frame Remove animation data from frame, operation complete Paste frame paste frame animation position, operation complete Paste keyframe Paste keyframe animation data complete Set Looping Change whether animation loops operation complete End animation mode operation complete Frame: n/a Delete Animation? window title Are you sure you want to delete this animation? Delete Animation Delete animation, operation complete No frame animation data to paste No skeletal animation data to paste AnimWidgetBase Animation FPS Play Stop Loop Frames X Delete Animation AnimWindow Animations AutoAssignJointWin F1 Help Shortcut AutoAssignJointWinBase Auto-Assign Bone Joints Only assign to selected joints Single Multiple Press F1 for help Ok Cancel BackgroundSelect All Supported Formats ( Open background image Could not open file BackgroundSelectBase None File... BackgroundWin F1 Help Shortcut Background Image operation complete BackgroundWinBase Select Background Image Front Back Left Right Top Bottom Press F1 for help Ok Cancel BoolPanel Boolean Operation BoolWin Union boolean operation Subtraction boolean operation Intersection boolean operation You must have at least once face selected Object A triangles are still selected Union With Selected boolean operation Subtract Selected boolean operation Intersect With Selected boolean operation Fuse Selected boolean operation Select faces to set Select faces to set as 'A' Object in boolean operation BoolWinBase Boolean Operation Operation Fuse Union Subtraction Intersection Set Object A Select faces to set Subtract Selected Cal3dPrompt F1 Help Shortcut Cal3dPromptBase Cal3D Filter Options Save Meshes All meshes in one file Group meshes by name Save Materials CRF (Binary) XRF (XML Text) Ok Cancel Command Align Selected... Cap Holes complete Could not find gap in selected region Cap Holes Selected primitives copied You must have at least 1 face, joint, or point selected to Copy Copy Selected to Clipboard Delete Deleting joints may destroy skeletal animations Do you wish to continue? Primitives deleted Selected primitives duplicated You must have at least 1 face, joint, or point selected to Duplicate Duplicate complete Duplicate Edge Divide complete You must have 2 adjacent vertices selected to Edge Divide Edge Divide Edge Turn complete You must have at least 2 adjacent faces to Edge Turn Edge Turn Extrude... Flatten Flatten X Flatten Y Flatten Z Need at least 1 vertex, joint, point, or face selected Selected primitives flattened Flip Flip X Flip Y Flip Z Selected primitives flipped Hide Hide Unselected Hide Selected Unhide All Selected primitives hidden Primitives unhidden Unselected primitives hidden Selection inverted Invert Selection Normals inverted Invert Normals Make Face From Vertices Face created Must select exactly 3 vertices Paste complete Nothing to paste Paste failed: %1 %2 Paste from Clipboard Rotate Texture Coordinates Face Group Texture coordinates rotated Must select faces Select Free Vertices Free-floating vertices selected Simplify Mesh Snap Vertices Together Snap All Selected Snap Nearest Selected Snap All and Weld Snap Nearest and Weld Spherify... Subdivide complete You must have at least 1 face selected to Subdivide Faces Subdivide Faces You must have 1 or more vertices selected to unweld. Unweld Vertices You must have 2 or more vertices selected to weld. Weld Vertices Unwelded %1 vertices into %2 vertices Welded %1 vertices into %2 vertices Normals Face Out Meshes Normals Vertices Faces CommandWidget You are in animation mode, but there are no animations ContextGroup <None> <New> New Group Name of new group, window title Enter new group name: Set Group operation complete Unset Group operation complete Set Material operation complete Set Projection operation complete ContextGroupBase Group Projection Name ... Material Name Group Material: Group Name Texture Projection ContextInfluences <None> Change Joint Assignment operation complete Change Influence Weight operation complete Change Influence Type operation complete <Mixed> multiple types of bone joint influence Custom bone joint influence Auto bone joint influence Remainder bone joint influence Auto: %1 Rem: %1 ContextInfluencesBase Weight Custom Auto Remaining <None> Joint <Mixed> ContextName Rename operation complete ContextNameBase Name ContextPanel Properties Window title ContextPosition Set Position operation complete ContextPositionBase Position Z Y X Dimensions ContextProjection Set Projection Type operation complete ContextProjectionBase Projection Type Cylinder Sphere Plane ... ContextRotation Set Rotation operation complete ContextRotationBase Rotation Z Y X CubeToolWidget Cube Segment CylinderToolWidget Segments Sides Width Scale EllipsoidToolWidget Smoothness: Faces: Sphere From Center Checkbox that indicates if ellipsoid is created from center or far corner ErrorObject Success Canceled Operation not supported for this file type Invalid argument (internal error) File does not exist Permission denied Could not open file Could not read from file File is the wrong type or corrupted Unsupported version File contains invalid data Unexpected end of file Unknown error Could not write file This operation is not supported Write not supported, try "Export..." Unrecognized file extension (unknown type) ExtrudeWin F1 Help Shortcut Extrude complete Extrude operation complete ExtrudeWinBase Extrude Extrude options X: Z: Y: Make Back Faces E&xtrude Press F1 for help Close GroupCleanBase Clean Up Merge identical materials Remove unused materials Merge identical groups Remove unused groups Ok Cancel GroupCleanWin F1 Help Shortcut Group Clean-up operation complete Merged %1 groups, %2 materials; Removed %3 of %4 groups, %5 of %6 materials GroupWinBase Groups No group New Rename Delete Select Faces In Group Unselect Faces In Group Faces Assign As Group Add To Group Texture No texture Press F1 for help Ok Cancel GroupWindow F1 Help Shortcut New group window title Enter new group name: Group name must be between 1 and %1 characters Bad group name window title Cannot change cannot change group name, window title You cannot change the default group name Smoothness: Max Angle: Group changes operation complete HelpWinBase Help Contents Back Forward Ok IqePrompt F1 Help Shortcut IqePromptBase IQE Filter Options Save Meshes Save Points as Bone Joints Save Skeleton Save Animations Press F1 for help Ok Cancel JointWin F1 Help Shortcut Rename joint window title Enter new joint name: Joint changes operation complete JointWinBase Joints None Rename Delete Selection Select Joint Vertices Select Unassigned Vertices Assign Selected to Joint Add Selected to Joint F1 for help Ok Cancel KeyConfig V Select Vertices Tool Shortcut F Select Faces Tool Shortcut C Select Connected Mesh Tool Shortcut G Select Groups Tool Shortcut B Select Bone Joints Tool Shortcut T Select Points Tool Shortcut M Move Tool Shortcut R Rotate Tool Shortcut H Hide Unselected Command Shortcut Shift+H Hide Selected Command Shortcut Delete Delete Command Shortcut Ctrl+D Duplicate Command Shortcut Ctrl+C Copy to Clipboard Command Shortcut Ctrl+V Paste from Clipboard Command Shortcut Insert Extrude Command Shortcut Ctrl+W Weld Command Shortcut Ctrl+N File | New Window Shortcut Ctrl+O File | Open Shortcut Ctrl+S File | Save Shortcut Ctrl+Q File | Quit Shortcut Home View | Frame All Shortcut Shift+Home View | Frame Selected Shortcut Ctrl+G Groups | Edit Groups Shortcut Ctrl+M Groups | Edit Materials Shortcut Ctrl+E Groups | Edit Texture Coordinates Shortcut Ctrl+B Joints | Assign Selected Shortcut Shift+U Unhide All Command Shortcut KeyValueWindowBase Edit Meta Data Name Value Ok Cancel LicenseWin GNU General Public License LowLevel Cannot delete root joint Cannot add or delete because you have frame animations. Try "Merge..." instead. Success Canceled Operation not supported for this file type Invalid argument (internal error, probably null pointer argument) File does not exist Permission denied Could not open file Could not read from file File is the wrong type or corrupted Unsupported version File contains invalid data Unexpected end of file Unknown error Invalid error code Could not write file This operation is not supported MM3D encountered an unexpected data size problem See Help->About to contact the developers The model has a texture that's width or height is not a power of two (2, 4, 8, .., 64, 128, 256, ..). Could not load Model contains no skeletal animations Model skeletons do not match This looks like a player model. Do you want to load all sections? Could not load texture Write not supported, try "Export..." Unrecognized file extension (unknown type) MD2 requires all groups to have the same material. MD2 export requires all faces to be grouped. MD3 export requires all faces to be grouped. MD3_PATH+filename is to long. Set meta data for MD3 export Point name is too large for MD3 export. Group name is too large for MD3 export. Texture filename is too long. MM3D does not support CAL3D files in XML format The file does not contain any mesh or animation data Set meta data for Cal3D export No data marked for saving as IQE. IQE requires all faces to be grouped. IQE requires points to only have one bone influence. Too many vertexes for MS3D export (max 65,536). Too many faces for MS3D export (max 65,536). Too many groups for MS3D export (max 255). Too many materials for MS3D export (max 255). Too many bone joints for MS3D export (max 255). Bone joints must have unique names for MS3D export. Too many vertexes for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Too many faces for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Set meta data for MS3D export Bone joints must have unique names for SMD export. SMD export requires points to only have one bone influence. Set meta data for SMD export Unsupported D3D version. Missing version in D3D model. Invalid D3D directives count. Missing line count in D3D model. Too few tokens on line in D3D model. Found primitive start without primitive end in D3D model. Line primitive type is not supported for D3D model. Triangle strip primitive type is not supported for D3D model. Triangle fan primitive type is not supported for D3D model. Unsupported primitive type in D3D model. Primitive end without start in D3D model. Incomplete triangle list before primitive end in D3D model. Vertex outside of primitive begin/end in D3D model. Primitive start without end in D3D model. D3D requires all groups to have the same material. MapDirectionBase Which direction? Set new texture coordinates from which direction? Front Back Left Right Top Bottom Ok Cancel Md3Prompt F1 Help Shortcut Md3PromptBase MD3 Filter Options Save as a Player Model (head.md3, upper.md3, lower.md3) Save animation.cfg Press F1 for help Ok Cancel MergeWinBase Merge Model Merge location Base Point <origin> Rotation Translation Merge Options Include textures Include animations Animation Options Append animations Merge if possible Press F1 for help Ok Cancel MergeWindow F1 Help Shortcut Merge models operation complete MetaWindow F1 Help Shortcut Name meta value key name Value meta value 'value' Change meta data operation complete MetaWindowBase Name Value Model Meta Data New Delete Press F1 for help Ok Cancel ModelViewBase ModelView Perspective Front Back Left Right Top Bottom Orthographic ModelViewport Could not load background %1 Use the middle mouse button to drag/pan the viewport OpenGL error = Invalid Value OpenGL error = Invalid Enum OpenGL error = Invalid Operation OpenGL error = Stack Overflow OpenGL error = Stack Underflow OpenGL error = Out Of Memory OpenGL error = Unknown Ms3dPrompt F1 Help Shortcut Ms3dPromptBase MS3D Filter Options Vertex Format Subversion 0 (Single bone joint influence) Subversion 1 (Multiple bone joints influences, weight scale 255) Subversion 2 (Multiple bone joints influences, weight scale 100) Subversion 3 (Multiple bone joints influences, weight scale 100) Subversion Options Vertex Extra Vertex Extra 2 Press F1 for help Ok Cancel NewAnimBase New Animation &Name Animation Type &Skeletal Alt+S &Frame Alt+F &Ok Alt+O &Cancel Alt+C ObjPrompt F1 Help Shortcut ObjPromptBase OBJ Filter Options &Save Normals Alt+S &Normal Decimal Places &Texture Decimal Places &Vertex Decimal Places Press F1 for help Ok Cancel PaintTextureWin F1 Help Shortcut File name for saved texture? File exists. Overwrite? Could not write file: PaintTextureWinBase Paint Texture Polygons: Edges Filled Filled and Edges Vertices Hidden Visible Clear Background Save Size: 64 128 256 512 1024 2048 x Save Texture... Press F1 for help Close PluginWinBase Plugin Version Description Status Plugins Filename Press F1 for help Ok PluginWindow F1 Help Shortcut PointWin F1 Help Shortcut Rename point window title Enter new point name: Point changes operation complete PointWinBase Points Rename Delete Bone Joint (none) F1 for help Ok Cancel PolyToolWidget Poly Type Strip Triangle strip option Fan Triangle fan option ProjToolWidget Type Cylinder Cylinder projection type Sphere Sphere projection type Plane Plane projection type ProjectionWin F1 Help Shortcut Set Projection Type operation complete Set Triangle Projection operation complete Apply Projection operation complete Reset UV Coordinates operation complete Rename projection window title Enter new point name: Rename Projection operation complete CTRL+Z Undo shortcut CTRL+Y Redo shortcut ProjectionWinBase Texture Projection Material Test Pattern Cylinder Sphere Plane Show: Type: Rename Zoom: Remove Faces Add Faces to Projection Apply Projection Reset UV Range Press F1 for help Close RotateToolWidget X Y Z ScaleToolWidget Proportion Free Free scaling option Keep Aspect 2D 2D scaling aspect option Keep Aspect 3D 3D scaling aspect option Point Center Scale from center Far Corner Scale from far corner SelectFaceToolWidget Include Back-facing SmdPrompt F1 Help Shortcut SmdPromptBase SMD Filter Options Model Type Reference Animation Save Points as Bone Joints Vertex Format GoldSrc (Single bone joint influence) Source (Multiple bone joint influences) Press F1 for help Ok Cancel SpherifyWin Spherify operation complete StatusBar V: Vertices status bar label F: Faces status bar label G: Groups status bar label B: Bone Joints status bar label P: Points status bar label M: Materials status bar label TextWinBase Ok TextureCoord Reset coordinates? window title Are you sure you want to reset texture coordinates for this group? Move texture coordinates F1 Help Shortcut CTRL+Z Undo shortcut CTRL+Y Redo shortcut Select texture coordinates TextureCoordBase Texture Coordinates Zoom: Mouse Tool Select Move Scale Scale Options Scale from center Keep aspect ratio Map Scheme Triangle Quad Group Reset Coordinates Press F1 for help Close Rotate Lines Black Blue Green Cyan Red Magenta Yellow White Selection Rotate CCW Rotate CW V Flip H Flip TextureWindow All Supported Formats ( all texture formats Open texture image Could not open file Enter new material name: New Material window title Rename material window title Texture changes Shininess Red Change texture... Change material's texture file Set texture... Add texture file to material F1 Help Shortcut TextureWindowBase Materials None New Material... Rename Delete Wrap X Clamp X Wrap Y Clamp Y Change Texture... X Remove texture Flat Preview 3D Preview Alpha Green Blue Red Ambient Diffuse Specular Emissive Shininess Press F1 for help Ok Cancel Tool Attract Far Attracting far selected primitives Attract far complete Attract Near Attracting near selected primitives Attract near complete Move Background Image Moving background image Cannot move background from 3D view Background move complete Move background image Scale Background Image Scaling background image Cannot scale background from 3D view Background scale complete Scale background image Cube created Create Cube Cylinder created Create Cylinder Ellipsoid created Create Ellipsoid Joint created Root joint created Create Bone Joint Moving selected primitives Move complete Tip: Hold shift to restrict movement to one dimension Move Point created Create Point Create Polygon Projection created Create Projection Rectangle created Create Rectangle Tip: Hold shift to rotate in 15 degree increments Rotating selected primitives Setting rotation point Rotate complete Rotate Scale Scaling selected primitives Scale complete Tip: Hold shift to restrict scaling to one dimension Starting selection Selection complete Select Bone Joints Select Connected Mesh Select Faces Select Groups Select Points Select Projections Select Vertices Shear Starting shear on selected primitives Shear complete Torus created Create Torus Vertex created Create Vertex Dragging selected vertex Must a vertex selected Drag complete Drag Vertex on Edge Extrude complete Extrude Must have faces selected to extrude Extruding selected faces Background Image Create Other Select Attract TorusToolWidget Segments Sides Width Circle From Center Checkbox that indicates if torus is created from center or from far corner TransformWindow Matrix Translate Matrix Rotate Matrix Rotate On Axis Matrix Scale Apply Matrix Transform Cannot Be Undone window title This transformation cannot be undone. Are you sure you wish to continue? Apply Transformation button Cancel Transformation button F1 Help Shortcut TransformWindowBase Transform Model X Y Z Translate Euler Angles Rotate Quaternion Angle Axis Scale (bottom row is translation) Apply Matrix Matrix Apply to: Press F1 for help Close Selected (including animations) Entire Model (including animations) ValueWin F1 Help Shortcut ValueWinBase Value Window Value Press F1 for help Ok Cancel ViewWindow Some models are unsaved. Save before exiting? Press F1 for help using any window Properties New File|New Save File|Save Run Script... File|Run Script Recent Scripts File|Recent Script Recent Models File|Recent Models Plugins... File|Plugins Close File|Close Quit File|Quit Hide Joints View|Hide Joints Draw Joint Lines View|Draw Joint Lines Draw Joint Bones View|Draw Joint Bones Draw Texture Projections View|Draw Texture Projections Hide Texture Projections View|Hide Texture Projections Use Red Error Texture View|Use Red Error Texture Use Blank Error Texture View|Use Blank Error Texture Render 3D Lines View|Render 3D Lines Hide 3D Lines View|Hide 3D Lines Draw Back-facing Triangles View|Draw Back-facing Triangles Hide Back-facing Triangles View|Hide Back-facing Triangles Frame All View|Frame Frame Selected View|Frame Show Properties View|Show Properties Render Options View|Render Options 3D Wireframe View|3D 3D Flat View|3D 3D Smooth View|3D 3D Texture View|3D 3D Alpha Blend View|3D Canvas Wireframe View|Canvas Canvas Flat View|Canvas Canvas Smooth View|Canvas Canvas Texture View|Canvas Canvas Alpha Blend View|Canvas 1 View View|Viewports 1x2 View View|Viewports 2x1 View View|Viewports 2x2 View View|Viewports 2x3 View View|Viewports 3x2 View View|Viewports 3x3 View View|Viewports Viewport Settings... View|Viewport Settings Grid Tools|Snap to Grid Vertex Tools|Snap to Vertex Undo Ctrl+Z Undo shortcut Redo Ctrl+Y Redo shortcut Snap To Tools Edit Joints... Joints|Edit Joints Assign Selected to Joint Joints|Assign Selected to Joint Remove All Influences from Selected Joints|Remove All Influences from Selected Remove Selected Joint from Influencing Joints|Remove Selected Joint from Influencing Convert Multiple Influences to Single Joints|Convert Multiple Influences to Single Select Joint Influences Joints|Select Joint Influences Select Influenced Vertices Joints|Select Influenced Vertices Select Influenced Points Joints|Select Influenced Points Select Unassigned Vertices Joints|Select Unassigned Vertices Select Unassigned Points Joints|Select Unassigned Points Start Animation Mode... Animation|Start Animation Mode Stop Animation Mode Animation|Stop Animation Mode Animation Sets... Animation|Animation Sets Copy Animation Frame Animation|Copy Animation Frame Paste Animation Frame Animation|Paste Animation Frame Clear Animation Frame Animation|Clear Animation Frame Set Rotation Keyframe Animation|Set Rotation Keyframe Set Translation Keyframe Animation|Set Translation Keyframe Clear Rotation Keyframe Animation|Clear Rotation Keyframe Clear Translation Keyframe Animation|Clear Translation Keyframe Contents... Help|Contents License... Help|License About... Help|About &File menu bar &View menu bar &Tools menu bar &Influences menu bar &Animation menu bar &Help menu bar All Files (*) Save model file as Open model file Merge models : Merge animations Script %1 complete Script %1 error %2 Save first? Model has been modified Do you want to save before closing? Unknown response: %1, Canceling close request Cannot hide with selected projections. Unselect projections now? Hide projections Cannot hide with selected joints. Unselect joints now? Hide bone joints You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'edit texture coordinates' window You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'paint texture' window Undo %1 Nothing to undo Redo %1 Nothing to redo This model does not have any animations Set rotation keframe Set translation keframe Clear rotation keframe Clear translation keframe Unknown response: Canceling operation [unnamed] For filename in title bar (if not set) Assigning %1 vertices and %2 points to joints Assign Selected to Joint You must have at least one bone joint selected. Remove All Influences from Selected Remove Joint from Influencing Convert To Single Influence Select Unassigned Vertices Select Unassigned Points Select Joint Influences Select Influences Vertices Select Influenced Points Auto-Assign Selected... Joints|Auto-Assign Selected Auto-Assign Selected to Bone Joints You must have at least one vertex or point selected. Open... File|Open Save As... File|Save As Export... File|Export Edit Model Meta Data... Model|Edit Model Meta Data Transform Model... Model|Transform Model Boolean Operation... Model|Boolean Operation Set Background Image... Model|Set Background Image Merge... Model|Merge Import Animations... Model|Import Animations Edit Groups... Materials|Edit Groups Edit Materials... Materials|Edit Materials Clean Up... Materials|Clean Up Reload Textures Materials|Reload Textures Edit Projection... Materials|Edit Projection Edit Texture Coordinates... Materials|Edit Texture Coordinates Paint Texture... Materials|Paint Texture Save Animation Images... Animation|Save Animation Images Copy Selected Keyframes Animation|Copy Animation Frame Paste Selected Keyframes Animation|Paste Animation Frame &Model menu bar &Geometry menu bar Mate&rials menu bar All Exportable Formats All Writable Formats Export model All Supported Formats model formats All Supported Formats F1 Help Shortcut Export Selected... File|Export Selected You must have at least 1 face, joint, or point selected to Export Selected ViewportSettings F1 Help Shortcut ViewportSettingsBase Viewport Settings Canvas Grid Default Grid Unit 3D Grid Grid Lines X/Y Plane X/Z Plane Y/Z Plane Press F1 for help &Ok Alt+O &Cancel Alt+C Decimal Grid Binary Grid Fixed Grid mm3d-1.3.15/translations/mm3d_sk.ts000066400000000000000000006305051466047437300171160ustar00rootroot00000000000000 AboutWin Maverick Model 3D - About Maverick Model 3D - O programe AlignWin F1 Help Shortcut Align X Zarovnať X Align Y Zarovnať Y Align Z Zarovnať Z Align Selected operation complete Zarovnať Označené AlignWinBase Align Selection Zarovnať výber Align X Zarovnať X Align minimum Zarovnať minimum Align center Zarovnať stred Align maximum Zarovnať maximum Align &X Now Zarovnať &X Teraz Alt+X Alt+X Align Y Zarovnať Y Align &Y Now Zarovnať &Y Teraz Alt+Y Alt+Y Align Z Zarovnať Z Align &Z Now Zarovnať &Z Teraz Alt+Z Alt+Z Press F1 for help Stlač F1 pre pomoc Ok Ok Cancel Zrušiť AnimConvertWinBase Convert To Frame Konvertovať na rámec Convert Skeletal to Frame: Konvertovať kostrovú na rámcovú: example Skeletal Animation Kostrová animácia Frame Animation Rámcová animácia Convert AnimName AnimName Frame Anim Name: Názov rámcovej animácie: Frame Count Počet rámcov TORSO_IDLE 1 F1 for help Stlač F1 pre pomoc Continue Pokračovať Cancel Zrušiť Cancel All Zrušiť všetko AnimConvertWindow F1 Help Shortcut Convert Skeletal to Frame: Konvertovať kostrovú na rámcovú: Skeletal Animation Kostrová animácia Convert Frame to Frame: Konvertovať rámcovú na rámcovú: Frame Animation Rámcová animácia Convert Frame Relative to Frame: Konvertovať rámcovú relatívnu na rámcovú: Frame Relative Animation Convert Unknown Type to Frame: Konvertovať neznámy typ na rámec: Unknown Type Animation AnimExportWinBase Save Animation Images Export animácie Source Zdroj Animation Animácia Viewport Výrez Duration Trvanie Iterations Cykly Seconds Sekúnd Output Výstup Directory Adresár Format Formát anim_0001.jpg anim_1.jpg anim_0001.png anim_1.png ... ... Frame Rate Rámcov za sekundu Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť Export Animation Export animácie AnimExportWindow Skeletal - Skeletal Animation prefix Kostrová - Frame - Frame Animation prefix Rámec - [None] No viewport for animation image export [Nie je] Viewport %1 - Výrez %1 - F1 Help Shortcut Must have more than 0 frames per second Musí mať viac ako 0 rámcov za sekundu Must have more than 0 seconds of animation Animácia musí mať viac ako 0 sekúnd Could not write file: Nemôžem zapísať súbor: Output directory does not exist. Výstupný adresár neexistuje. AnimSetWinBase Animation Sets Množina animácií &Down &Dole &Up &Hore &New &Nový &Rename &Premenuj D&elete &Vymazať &Copy &Kopírovať &Split &Rozdeľ &Join &Spojiť &Merge &Zlúčiť Con&vert To Frame Animation Kon&vertovať na rámcovú animáciu Press F1 for help Stlač F1 pre pomoc Ok Ok Cancel Zrušiť AnimSetWindow Skeletal Animation Kostrová animácia Frame Animation Rámcová animácia F1 Help Shortcut New Animation Nová animácia New name: Nové meno: Rename Animation Split at frame Split animation frame window title Rozdeľ na rámeci Split 'Split' refers to splitting an animation into two separate animations Rozdeľ at frame number the frame number where the second (split) animation begins za rámcom číslo split rozdeľ Cannot Split Cannot split animation window title Nemôžem rozdeliť Must have at least 2 frames to split split animation Na rozdelenie treba najmenej 2 rámce Cannot merge animation %1 and %2, frame counts differ. Nemôžem zlúčiť animáciu %1 a %2, počet rámcov sa líši. Can only merge skeletal animations. Môžem zlučovať iba kostrové animácie. Animation changes operation complete Zmeny animácie copy kopírovať AnimWidget <New Animation> <Nová animácia> Start animation mode operation complete Spusti režim animácie New Animation operation complete Nová animácia Frame: Rámec: Set FPS Frames per second, operation complete Nastav FPS Change Frame Count operation complete Zmeniť počet rámcov Clear frame Remove animation data from frame, operation complete Vymazať rámec Paste frame paste frame animation position, operation complete Vložiť rámec Paste keyframe Paste keyframe animation data complete Vložiť kľúčový rámec Set Looping Change whether animation loops operation complete End animation mode operation complete Ukončiť režim animácie Frame: n/a Rámec: nie je Delete Animation? window title Vymazať animáciu? Are you sure you want to delete this animation? Naozaj vymazať túto animáciu? Delete Animation Delete animation, operation complete Vymazať animáciu No frame animation data to paste V schránke nie sú dáta rámcovej animácie No skeletal animation data to paste V schránke nie sú dáta kostrovej animácie AnimWidgetBase Animation Animácia FPS Play Spustiť Stop Zastaviť Loop Slučka Frames Rámce X Delete Animation Vymazať animáciu AnimWinBase Animation Animácia New Nový Rename Premenovať Delete Vymazať Keyframe Kľúčový rámec Frames Rámce Frame: 03 Rámec: 03 Copy Frame Kopírovať Paste Frame Vložiť rámec Clear Frame Výmazať rámec Skeletal Kostrová Frame Rámec Play Spustiť Stop Zastaviť Loop Slučka Press F1 for help Stlač F1 pre pomoc Close Zatvoriť AnimWindow Misfit 3D Misfit 3D New name: Nové meno: Animations Animácie AutoAssignJointWin F1 Help Shortcut AutoAssignJointWinBase Auto-Assign Bone Joints Automaticky priradiť kĺby Only assign to selected joints Iba priradiť k zvoleným kĺbom Single Jeden Multiple Viac Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť BackgroundSelect All Supported Formats ( Všetky podporované formáty ( Open background image Otvoriť obrázok pozadia All Files (*) Všetky súbory (*) Could not open file Namôžem otvoriť súbor BackgroundSelectBase None Nič File... Súbor... BackgroundWin F1 Help Shortcut Background Image operation complete Obrázok pozadia BackgroundWinBase Select Background Image Zvoliť obrázok pozadia Front Predok Back Zozadu Left V ľavo Right V pravo Top Vrch Bottom Spodok Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť BoolPanel Boolean Operation Booleovské operácie BoolWin Union boolean operation Zjednotenie Subtraction boolean operation Odčítanie Intersection boolean operation Prienik You must have at least once face selected Musíte zvoliť aspoň jednu plochu Object A triangles are still selected Trojúholníky objektu A sú stále vybrané Union With Selected boolean operation Zjednotiť s vybranými Subtract Selected boolean operation Odčítať zvolené Intersect With Selected boolean operation Prienik so zvolenými Fuse Selected boolean operation Spojiť vybrané Select faces to set Select faces to set as 'A' Object in boolean operation Vybrať plochy do množiny BoolWinBase Boolean Operation Boolovské operácie Operation Operácia Fuse Spojiť Union Zjednotenie Subtraction Odčítať Intersection Prienik Set Object A Nastaviť objekt A Select faces to set Vybrať plochy do množiny Subtract Selected Odčítať zvolené Cal3dPrompt F1 Help Shortcut Cal3dPromptBase Cal3D Filter Options Nastavenia Cal3D filtra Save Meshes Uložiť mriežky All meshes in one file Všetky mriežky v jednom súbore Group meshes by name Zoskupiť mriežky podľa mena Save Materials Uložiť materiály CRF (Binary) CRF (Binárny) XRF (XML Text) XRF (XML Text) Ok Ok Cancel Zrušiť Command Align Selected... Zarovnať označené... Cap Holes complete Povrchové dierky ukončené Could not find gap in selected region Neviem nájsť otvor vo zvolenej oblasti Cap Holes Povrchové dierky Selected primitives copied Zvolené útvary zkopírované You must have at least 1 face, joint, or point selected to Copy Musíte zvoliť najmenej 1 plochu, uzol alebo bod na Kopírovanie Copy complete Kopírovanie ukončené Copy Selected to Clipboard Kopírovať označené do schránky Delete Vymazať Deleting joints may destroy skeletal animations Do you wish to continue? Vymazanie kĺbov môže zničiť kostrovú animáciu Chcete pokračovať? Primitives deleted Útvary vymazané Selected primitives duplicated Zvolené útvary zdvojené You must have at least 1 face, joint, or point selected to Duplicate Musíte zvoliť najmenej 1 plochu, uzol alebo bod na Duplikovanie Duplicate complete Zdvojenie ukončené Duplicate Zdvojiť Edge Divide complete Rozdelenie okraja dokončené You must have 2 adjacent vertices selected to Edge Divide Musíte zvoliť najmenej 2 susedné vrcholy na Rozdelenie plochy Edge Divide Rozdeliť okraj Edge Turn complete Rotovanie okraja dokončené You must have at least 2 adjacent faces to Edge Turn Musíte zvoliť najmenej 2 susedné plochy na vykonanie Rotácie okraja Edge Turn Rotovať okraj Extrude... Vysunúť... Flatten Sploštiť Flatten X Sploštiť X Flatten Y Sploštiť Y Flatten Z Sploštiť Z Need at least 1 vertex, joint, point, or face selected Musíte zvoliť aspoň 1 vrchol, kĺb, alebo plochu Selected primitives flattened Zvolené útvary sploštené Flip Preklopiť Flip X Preklopiť X Flip Y Preklopiť Y Flip Z Preklopiť Z Selected primitives flipped Zvolené útvary otočené Hide Schovať Hide Unselected Schovať nezvolené Hide Selected Schovať zvolené Unhide All Odkryť všetko Selected primitives hidden Zvolené útvary schované Primitives unhidden Útvary odkryté Unselected primitives hidden Odznačiť skrýté útvary Selection inverted Invertovať výber Invert Selection Invertovať označenie Normals inverted Normály invertované Invert Normals Invertovať normály Make Face From Vertices Vytvoriť plochy z vrcholov Face created Plocha vytvorená Must select exactly 3 vertices Musíte zvoliť práve 3 plochy Paste complete Vloženie dokončené Nothing to paste Paste failed: %1 %2 Paste from Clipboard Vložiť zo schránky Rotate Texture Coordinates Otočiť súradnice textúry Face Plocha Group Skupina Texture coordinates rotated Rotovať súradnice textúry Must select faces Musíte zvoliť plochy Select Free Vertices Zvoliť voľné vrcholy Free-floating vertices selected Voľne plávajúce vrcholy zvolené Simplify Mesh Zjednodušiť mriežku Snap Vertices Together Prichytiť vrcholy dokopy Snap All Selected Prichytiť všetky zvolené Snap Nearest Selected Prichytiť najbližšie zvolené Snap All and Weld Prichytiť a spojiť všetko Snap Nearest and Weld Prichytiť najbližšie a spojiť Spherify... Zaguľatiť... Subdivide complete Rozdelenie plôch dokončené You must have at least 1 face selected to Subdivide Faces Subdivide Faces Ďalej rozdeliť plochy You must have 1 or more vertices selected to unweld. Musíte zvoliť 1 alebo viac vrcholov na Rozdelenie vrcholov. Unweld Vertices Rozdeliť vrcholy You must have 2 or more vertices selected to weld. Musíte mat 2 alebo viac vrcholov na Spojenie vrcholov. Weld Vertices Spojit vrcholy Unwelded %1 vertices into %2 vertices Rozdelené %1 vrcholov na %2 vrcholov Welded %1 vertices into %2 vertices Spojené %1 vrcholov do %2 vrcholov Normals face out Normálami von Vertices Vrcholy Faces Plochy Meshes Mriežky Normals Normály Normals Face Out Normálami von CommandWidget You are in animation mode, but there are no animations Ste v režime animácie, ale nie sú tu žiadne animácie ContextGroup <None> <Žiadne> <New> <Nové> New Group Name of new group, window title Nová skupina Enter new group name: Zadaj meno novej skupiny: Set Group operation complete Nastaviť skupiny Unset Group operation complete Odobrať skupinu Set Material operation complete Nastaviť materiál Set Projection operation complete Nastaviť premietanie ContextGroupBase Group Skupina Projection Name Meno premietania ... ... Material Name Meno materiálu Group Material: Materiál skupiny: Group Name Meno skupiny Triangle Projection Trojúholníkové premietanie Texture Projection Premietanie textúry ContextInfluences <None> <Nič> Change Joint Assignment operation complete Zmeniť priradenie kĺbu Change Influence Weight operation complete Zmeniť pôsobenie hmotnosti Change Influence Type operation complete Zmeniť typ pôsobenia <Mixed> multiple types of bone joint influence <Rôzne> Custom bone joint influence Rôzne Auto bone joint influence Automaticky Remainder bone joint influence Zvyšok Auto: %1 Automaticky: %1 Rem: %1 Ešte: %1 ContextInfluencesBase Weight Hmotnosť <Mixed> <Zmiešané> Custom Rôzne Auto Automaticky Remaining Zostávajúce <None> <Žiadne> Joint Kĺb ContextName Rename operation complete Premenovať ContextNameBase Name Meno ContextPanel Properties Window title Vlastnosti ContextPosition Set Position operation complete Nastaviť polohu ContextPositionBase Position Poloha Z Y X Dimensions Rozmery ContextProjection Set Projection Type operation complete Nastaviť typ zobrazenia ContextProjectionBase Projection Type Typ premietania Cylinder Valec Sphere Guľa Plane Rovina ... ... ContextRotation Set Rotation operation complete Nastaviť otočenie ContextRotationBase Rotation Otočenie Z Y X CubeToolWidget Cube Kocka Segment Články CylinderToolWidget Segments Články Sides Strany Width Šírka Scale Mierka DeleteCommand Deleting joints may destroy skeletal animations Do you wish to continue? Zmazanie kĺbov môže zničiť kostrovú animáciu Chcete pokračovať? Primitives deleted Útvary zmazané EditKeyframeWin Set keyframe %1 Nastaviť kľúčový rámec EllipsoidToolWidget Smoothness: Hladkosť: Faces: Plochy: Sphere Guľa From Center Checkbox that indicates if ellipsoid is created from center or far corner Od stredu ErrorObject Success Hotovo Canceled Zrušené File is an unknown type Súbor je neznámeho typu Operation not supported for this file type Operácia nie je podporovaná pre tento typ súborov Invalid argument (internal error) Chybný argument (vnútorná chyba) File does not exist Súbor neexistuje Permission denied Prístup zamietnutý Could not open file Nemôžem otvoriť súbor Could not read from file Nemôžem čítať zo súboru File is the wrong type or corrupted Súbor má nesprávny typ alebo je poškodený Unsupported version Nepodporovaná verzia File contains invalid data Súbor obsahuje chybné údaje Unexpected end of file Neočakávaný koniec súboru Unknown error Voľačo sa posralo Could not write file Nemôžem zapisovať do súboru This operation is not supported Táto operácie nie je podporovaná Write not supported, try "Export..." Zápis nie je podporovaný, skúste "Export..." Unrecognized file extension (unknown type) Nerozpoznaná prípona súboru (neznámy typ) ExtrudeWin F1 Help Shortcut Extrude complete Vysunutie dokončené Extrude operation complete Vysunutie ExtrudeWinBase Extrude Vysunutie Extrude options Nastavenie vysunutia X: X: Z: Z: Y: Y: Make Back Faces Vytvoriť zadné plochy E&xtrude Vys&unutie Press F1 for help Stlač F1 pre help Close Zatvoriť GroupCleanBase Group Clean-up Window Okno vyčistenia skupín Clean Up Merge identical materials Zlúčiť identické materiály Remove unused materials Odstrániť nepoužité materiály Merge identical groups Zlúčiť identické skupiny Remove unused groups Odstrániť nepoužité skupiny Ok Ok Cancel Zrušiť GroupCleanWin F1 Help Shortcut Group Clean-up operation complete Vyčistiť skupiny Merged %1 groups, %2 materials; Removed %3 of %4 groups, %5 of %6 materials GroupWinBase Groups Skupiny No group Žiadna skupina New Nový Rename Premenovať Delete Vymazať Select Faces In Group Zvoliť plochy v skupine Unselect Faces In Group Odznačiť plochy v skupine Faces Plochy Assign As Group Priradiť ako skupinu Add To Group Pridať do skupiny Texture Textúra No texture Žiadna textúra Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť GroupWindow F1 Help Shortcut New group window title Nová skupina Enter new group name: Zadajte nové meno skupiny: Group name must be between 1 and %1 characters Meno skupiny musí byť medzi 1 a %1 znakmi Bad group name window title Zlé meno skupiny Cannot change cannot change group name, window title Nemožno zmeniť You cannot change the default group name Nemožno zmeniť predvolné meno skupiny Smoothness: Hladkosť: Max Angle: Max. uhol: Group changes operation complete Zmeny skupiny HelpWinBase Help Pomoc Contents Obsah Back Späť Forward Dopredu Ok IqePrompt F1 Help Shortcut IqePromptBase IQE Filter Options Save Meshes Uložiť mriežky Save Points as Bone Joints Save Skeleton Save Animations Press F1 for help Ok Ok Cancel Zrušiť JointWin F1 Help Shortcut Rename joint window title Premenovať kĺb Enter new joint name: Zadaj meno nového kĺbu: Joint changes operation complete Zmeny kĺbov JointWinBase Joints Kĺby None Nič Rename Premenovať Delete Vymazať Selection Výber Select Joint Vertices Zvoliť vrcholy kĺbu Select Unassigned Vertices Zvoliť nepriradené vrcholy Assign Selected to Joint Priradiť označené ku kĺbu Add Selected to Joint Pridať zvolené ku kĺbu F1 for help F1 pre pomoc Ok Cancel Zrušiť KeyConfig V Select Vertices Tool Shortcut F Select Faces Tool Shortcut C Select Connected Mesh Tool Shortcut G Select Groups Tool Shortcut B Select Bone Joints Tool Shortcut T Select Points Tool Shortcut M Move Tool Shortcut R Rotate Tool Shortcut H Hide Unselected Command Shortcut Shift+H Hide Selected Command Shortcut ? Unhide All Command Shortcut ? Delete Delete Command Shortcut Ctrl+D Duplicate Command Shortcut Ctrl+C Copy to Clipboard Command Shortcut Ctrl+V Paste from Clipboard Command Shortcut Insert Extrude Command Shortcut Ctrl+W Weld Command Shortcut Ctrl+N File | New Window Shortcut Ctrl+O File | Open Shortcut Ctrl+S File | Save Shortcut Ctrl+Q File | Quit Shortcut Home View | Frame All Shortcut Shift+Home View | Frame Selected Shortcut Ctrl+G Groups | Edit Groups Shortcut Ctrl+M Groups | Edit Materials Shortcut Ctrl+E Groups | Edit Texture Coordinates Shortcut Ctrl+B Joints | Assign Selected Shortcut Shift+U Unhide All Command Shortcut KeyValueWindowBase Edit Meta Data Editovať meta dáta Name Meno Value Hodnota Ok Cancel Zrušiť LicenseWin GNU General Public License LowLevel Success Hotovo Canceled Zrušené File is an unknown type Súbor je neznámeho typu Operation not supported for this file type Operácia nie je podporovaná pre tento typ súboru Invalid argument (internal error, probably null pointer argument) Chybný argument (cnútorná chyba, pravdeporobne null pointer argument) File does not exist Súbor neexistuje Permission denied Prístup odmietnutý Could not open file Nemôžem otvoriť súbor Could not read from file Nemôžem čítať zo súboru File is the wrong type or corrupted Súbor je nesprávneho typu alebo je poškodený Unsupported version Nepodporovaná verzia File contains invalid data Súbor obsahuje chybné dáta Unexpected end of file Neočakávaný koniec súboru Unknown error Neznáma chyba Invalid error code Nesprávny chybový kód Cannot delete root joint Nemožno zmazať hlavný kĺb Cannot add or delete because you have frame animations. Try "Merge..." instead. Nemožno pridať alebo zmazať lebo máte rámcovú animáciu. Skúste namiesto toho "Zlúčiť...". Could not write file Nemožno zapísať súbor This operation is not supported Táto operácia nie je podporovaná MM3D encountered an unexpected data size problem See Help->About to contact the developers MM3D narazil na problém dát neočakávanej veľkosti Pozrite Pomoc->O programe na kontakt s vývojármi The model has a texture that's width or height is not a power of two (2, 4, 8, .., 64, 128, 256, ..). Model má textúru ktorá nie je mocninou 2 Could not load Nemožno načítať Model contains no skeletal animations Model neobsahuje kostrové animácie Model skeletons do not match Kostry modelu sa nezhodujú This looks like a player model. Do you want to load all sections? Toto vyzerá ako model hráča. Chcete načítať všetky sekcie? Could not load texture Nemožno načítať textúry Write not supported, try "Export..." Zápis nie je podporovaný, skúste "Export..." Unrecognized file extension (unknown type) Nerozpoznaná prípona súboru (neznámy typ) MD2 requires all groups to have the same material. MD2 vyžaduje aby všetky skupiny mali rovnaký materiál. MD2 export requires all faces to be grouped. MD2 export vyžaduje aby všetky plochy boli v jednej skupine. MD3 export requires all faces to be grouped. MD3 export vyžaduje aby všetky plochy boli v nejakej skupine. MD3_PATH+filename is to long. MD3_PATH+meno súboru je príliž dlhé. Too many animation frames for MD3 export. Príliš veľa animácií pre MD3 export. Too many points for MD3 export. Príliš veľa bodov pre MD3 export. Too many groups for MD3 export. Príliš veľa skupín pre MD3 export. Too many faces in a single group for MD3 export Príliš veľa plôch v jednej skupine pre MD3 export Too many vertices in a single group for MD3 export Príliš veľa vrcholov v jednej skupine pre MD3 export Set meta data for MD3 export Point name is too large for MD3 export. Meno bodu je príliš dlhé pre MD3 export. Group name is too large for MD3 export. Meno skupiny je príliš dlhé pre MD3 export. Texture filename is too long. Meno textúry je príliš dlhé. MM3D does not support CAL3D files in XML format MM3D nepodporuje CAL3D v XML formáte The file does not contain any mesh or animation data Súbor neobsahuje žiadnu mriežku ani aimáciu Set meta data for Cal3D export No data marked for saving as IQE. IQE requires all faces to be grouped. IQE requires points to only have one bone influence. Too many vertexes for MS3D export (max 65,536). Too many faces for MS3D export (max 65,536). Too many groups for MS3D export (max 255). Too many materials for MS3D export (max 255). Too many bone joints for MS3D export (max 255). Bone joints must have unique names for MS3D export. Too many vertexes for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Too many faces for MS3D export (max 65,536) after duplicating vertexes used by multiple groups or materials. Set meta data for MS3D export Bone joints must have unique names for SMD export. SMD export requires points to only have one bone influence. Set meta data for SMD export Unsupported D3D version. Missing version in D3D model. Invalid D3D directives count. Missing line count in D3D model. Too few tokens on line in D3D model. Found primitive start without primitive end in D3D model. Line primitive type is not supported for D3D model. Triangle strip primitive type is not supported for D3D model. Triangle fan primitive type is not supported for D3D model. Unsupported primitive type in D3D model. Primitive end without start in D3D model. Incomplete triangle list before primitive end in D3D model. Vertex outside of primitive begin/end in D3D model. Primitive start without end in D3D model. D3D requires all groups to have the same material. MapDirectionBase Which direction? Ktorý smer? Set new texture coordinates from which direction? Z ktorej strany nastaviť nové súradnice textúry? Front Predok Back Zozadu Left Vľavo Right Vpravo Top Zhora Bottom Spodok Ok Cancel Zrušiť Md3Prompt F1 Help Shortcut Md3PromptBase MD3 Filter Options Save as a Player Model (head.md3, upper.md3, lower.md3) Save animation.cfg Press F1 for help Ok Ok Cancel Zrušiť MergeWinBase Merge Model Zlúčiť model Merge location Zlúčiť polohu Base Point <origin> Rotation Otočenie Translation Posunutie Merge Options Zlúčiť nastavenia Include textures Zahrnúť textúry Include animations Zahrnúť animáciu Animation Options Možnosti animácie Append animations Pridaná animácia Merge if possible Zlúčiť ak je to možné Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť MergeWindow F1 Help Shortcut Merge models operation complete Zlúčiť modely MetaWindow F1 Help Shortcut Name meta value key name Názov Value meta value 'value' Hodnota Change meta data operation complete Zmenit metadáta MetaWindowBase Name Meno Value Hodnota Model Meta Data Metadáta modelu New Nový Delete Vymazať Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť ModelViewBase ModelView Perspective Perspektíva Front Predok Back Zozadu Left Zľava Right Zprava Top Zhora Bottom Spodok Orthographic Pravoúhle ModelViewport Could not load background %1 Nemožno načítať pozadie %1 Use the middle mouse button to drag/pan the viewport Použite stredné tlačidlo myši na posunutie/naklonenie výrezu OpenGL error = Invalid Value Chyba OpenGL = Chybná hodnota OpenGL error = Invalid Enum Chyba OpenGL = Cybný výčtový typ OpenGL error = Invalid Operation Chyba OpenGL = Chybná operácia OpenGL error = Stack Overflow Chyba OpenGL = Pretečenie zásobníka OpenGL error = Stack Underflow Chyba OpenGL = Podtečenie zásobníka OpenGL error = Out Of Memory Chyba OpenGL = Nedostatok pamäte OpenGL error = Unknown Chyba OpenGL = Neznáma Ms3dPrompt F1 Help Shortcut Ms3dPromptBase MS3D Filter Options Nastavenie MS3D filtra Vertex Format Subversion 0 (Single bone joint influence) Subversion 1 (Multiple bone joints influences, weight scale 255) Subversion 2 (Multiple bone joints influences, weight scale 100) Subversion 3 (Multiple bone joints influences, weight scale 100) Subversion Options Nastavenie podverzie Vertex Extra Vrchol zvlášť Vertex Extra 2 Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť NewAnimBase New Animation Nová animácia &Name &Meno Animation Type Typ animácie &Skeletal &Kostrová Alt+S Alt+K &Frame &Rámcová Alt+F Alt+R &Ok &Ok Alt+O &Cancel &Zrušiť Alt+C Alt+Z ObjPrompt F1 Help Shortcut ObjPromptBase OBJ Filter Options Nastavenie filtra OBJ &Save normals &Uložiť normály &Save Normals Alt+S &Normal Decimal Places Desatinné miesta &normál &Texture Decimal Places Desatinné miesta &textúr &Vertex Decimal Places Desatinné miesta &vrcholov Press F1 for help Ok Cancel Zrušiť PaintTextureWin F1 Help Shortcut File name for saved texture? Meno súboru pre uložené textúry? File exists. Overwrite? Súbor existuje. Prepísať? Could not write file: Nemôžem zapísať súbor: PaintTextureWinBase Paint Texture Nakresliť textúru Polygons: Mnohoúholníky: Edges Okraje Filled Vyplnené Filled and Edges Vyplnené a okraje Vertices Vrcholy Hidden Schované Visible Viditeľlné Clear Background Vymazať pozadie Save Size: Uložiť veľkost: 64 128 256 512 1024 2048 x Save Texture... Uložiť textúru... Press F1 for help Stlač F1 pre pomoc Close Zatvoriť PluginWinBase Plugin Plugin Version Verzia Description Popis Status Stav Plugins Pluginy Filename Press F1 for help Stlač F1 pre pomoc Ok PluginWindow F1 Help Shortcut PointWin F1 Help Shortcut Rename point window title Premenovať bod Enter new point name: Zadaj nové meno bodu: Point changes operation complete Zmeny bodu PointWinBase Points Body Rename Premenovať Delete Vymazať Bone Joint Kĺb kosti (none) (nič) F1 for help F1 pre pomoc Ok Cancel Zrušiť PolyToolWidget Fan Vejár Poly Type Typ poly Strip Triangle strip option Pruh Fan Triangle fan option Vejár ProjToolWidget Type Typ Cylinder Cylinder projection type Valec Sphere Sphere projection type Guľa Plane Plane projection type Rovina ProjectionWin F1 Help Shortcut Set Projection Type operation complete Nastaviť typ premietania Set Triangle Projection operation complete Nastaviť trojúholníkové premietanie Apply Projection operation complete Aplikovať premietanie Reset UV Coordinates operation complete Východzie UV premietanie Rename projection window title Premenovať premietanie Enter new point name: Zadajte nové meno bodu: Rename Projection operation complete Premenovať premietanie CTRL+Z Undo shortcut CTRL+Y Redo shortcut ProjectionWinBase Texture Projection Projekcia textúry Material Materiál Test Pattern Testovací vzor Cylinder Valec Sphere Guľa Plane Rovina Show: Ukázať: Type: Typ: Rename Premenovať Zoom: Zväčšenie: Remove Faces Odstrániť plochy Add Faces to Projection Pridať plochy do premietania Apply Projection Aplikovať premietanie Reset UV Range Vynulovať UV rozsah Press F1 for help Stlač F1 pre pomoc Close Zatvoriť RgbaWinBase RGBA Window RGBA Okno Light Property Parametre svetla Red Červená Green Zelená Blue Modrá Alpha Priehľadnosť Close Zatvoriť RotateToolWidget X Y Z ScaleToolWidget Proportion Pomer Free Free scaling option Voľné Keep Aspect 2D 2D scaling aspect option Uchovať 2D pomer Keep Aspect 3D 3D scaling aspect option Uchovať 3D pomer Point Bod Center Scale from center Stred Far Corner Scale from far corner Vzdialený roh SelectFaceToolWidget Include Back-facing Plochy kresliť obojstranne SmdPrompt F1 Help Shortcut SmdPromptBase SMD Filter Options Model Type Reference Animation Animácia Save Points as Bone Joints Vertex Format GoldSrc (Single bone joint influence) Source (Multiple bone joint influences) Press F1 for help Ok Ok Cancel Zrušiť SpherifyWin Spherify operation complete Zaguľatiť StatusBar V: Vertices status bar label V: F: Faces status bar label P: G: Groups status bar label S: B: Bone Joints status bar label K: P: Points status bar label B: M: Materials status bar label M: TextWinBase Ok TextureCoord Reset coordinates? window title Vynulovať koordináty? Are you sure you want to reset texture coordinates for this group? Ste si istý že chcete vymazať súradnice textúry pre rúro skupinu? Move texture coordinates Presunúť súradnice textúry F1 Help Shortcut CTRL+Z Undo shortcut CTRL+Y Redo shortcut Select texture coordinates Zvoľte súradnice textúry TextureCoordBase Texture Coordinates Poloha textúry Zoom: Zväčšenie: Mouse Tool Nástroj myši Select Zvoliť Move Presunúť Scale Zmenšiť Scale Options Nastavenie mierky Scale from center Mierka od stredu Keep aspect ratio Zachovať pomer Map Scheme Schéma mapy Triangle Trojuholník Quad Obdĺžnik Group Skupina Reset Coordinates Vynulovať koordináty Press F1 for help Stlač F1 pre pomoc Close Zatvoriť Rotate Otočiť Lines Čiary Black Čierna Blue Modrá Green Zelená Cyan Tyrkisová Red Červená Magenta Fialová Yellow Žltá White Biela Selection Výber Rotate CCW Otočiť proti Rotate CW Otočiť V Flip V preklopiť H Flip H preklopiť TextureWindow All Supported Formats ( all texture formats Všetky podporované formáty ( Open texture image Otvoriť obrázok textúry All Files (*) Všetky súbory (*) Could not open file Nemôžem otvoriť súbor Color Material window title Farba materiálu Enter new material name: Zadajte meno nového materiálu: Rename texture window title Premenovať textúru Enter new texture name: Zadajte meno novej textúry: New Material window title Rename material window title Texture changes Zmeny textúry Shininess Lesk Red Červená Change texture... Change material's texture file Zmeniť textúru... Set texture... Add texture file to material Nastaviť textúru... F1 Help Shortcut TextureWindowBase Materials Materiály None Nič New Material... Nový materiál... Rename Premenovať Delete Vymazať Wrap X Zalomiť X Clamp X Prichytiť X Wrap Y Zalomiť Y Clamp Y Prichytiť Y Change Texture... Zmeniť textúru... X Remove texture Odstrániť textúru Flat Preview Plochý náhľad 3D Preview 3D náhľad Alpha Priehľadnosť Green Zelená Blue Modrá Red Červená Ambient Pozadie Diffuse Rozptýlená Specular Zrkadlová Emissive Žiarivá Shininess Lesk Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť Tool Attract Far Pritiahnuť zďaleka Attracting far selected primitives Pritiahnuť zdaleka zvolené útvary Attract far complete Pritiahnuť zďaleka ukončené Attract Near Priťiahnuť zblízka Attracting near selected primitives Pritiahnuť z blízka zvolené útvary Attract near complete Pritiahnuť zblízka hotové Move Background Image Presunúť obrázok pozadia Moving background image Presúvam obrázok pozadia Cannot move background from 3D view Namožno presúvať pozadie v 3D pohľade Background move complete Presun pozadia ukončený Move background image Presunúť obrázok pozadia Scale Background Image Zmenšiť obrázok pozadia Scaling background image Zmena mierky obrázka Cannot scale background from 3D view Nemožno meniť mierku pozadia v 3D pohlade Background scale complete Zmena veľkosti pozadia ukončená Scale background image Zmenšiť obrázok pozadia Cube created Kocka vytvorená Create Cube Vytvoriť kocku Cylinder created Valec vytvorený Create Cylinder Vytvoriť valec Ellipsoid created Elipsoid vytvorený Create Ellipsoid Vytvoriť elipsoid Joint created Kĺb vytvorený Root joint created Koreňový kĺb vytvorený Create Bone Joint Vytvoriť kĺb kosti Moving selected primitives Presúvam zvolené útvary Move complete Presun ukončený Tip: Hold shift to restrict movement to one dimension Tip: Podržte shift na obmedzenie pohybu do jedného smeru Move Presun Point created Bod vytvorený Create Point Vyutvoriť bod Create Polygon Vytvoriť mnohoúholník Projection created Premietanie vytvorené Create Projection Vytvoriť projekciu Rectangle created Obdĺžník vytvorený Create Rectangle Vytvoriť obdĺžník Tip: Hold shift to rotate in 15 degree increments Tip: Podržte shift na otočenie v 15 stupňových krokoch Rotating selected primitives Otáčam zvolené útvary Setting rotation point Nastavujem bod rotácie Rotate complete Otočenie dokončené Rotate Otočiť Scale Zmenšiť Scaling selected primitives Zmena veľkosti zvolených útvarov Scale complete Zmenšenie ukončené Tip: Hold shift to restrict scaling to one dimension Tip: Podržte shift na obmedzenie zmeny veľkosti do jedného smeru Starting selection Začínam výber Selection complete Oznacenie dokončené Select Bone Joints Označiť kĺby kostí Select Connected Mesh Označiť spojené mriežky Select Faces Vybrať plochy Select Groups Vybrať skupinu Select Points Vybrať body Select Projections Označiť premietanie Select Vertices Označiť vrcholy Shear Strihať Starting shear on selected primitives Začínam strihať vybrané útvary Shear complete Strihanie ukončené Torus created Prstenec vytvorený Create Torus Vytvoriť prstenec Vertex created Vrchol vytvorený Create Vertex Vytvoriť vrchol Dragging selected vertex Preťahujem zvolené vrcholy Must a vertex selected Musíte zvoliť vrchol Drag complete Pretiahnutie dokončené Drag Vertex on Edge Presunúť vrchol na okraj Extrude complete Vysunutie ukončené Extrude Vysunutie Must have faces selected to extrude Na vysunutie musíte zvoliť plochy Extruding selected faces Plochy na vysunutie zvolené Select Zvoliť Attract Priťiahnuť Background Image Obrázok pozadia Create Other Vytvoriť ostatné TorusToolWidget Segments Články Sides Strany Width Šírka Circle Kruh From Center Checkbox that indicates if torus is created from center or from far corner Zo stredu TransformWindow Matrix Translate Matica posunutia Matrix Rotate Maticu rotovať Matrix Rotate On Axis Maticu rotovať na osi Matrix Scale Matica zmeny mierky Apply Matrix Aplikovať maticu Transform Cannot Be Undone window title Transformáciu nie je možné vziať späť This transformation cannot be undone. Túto transformáciu nebude možné vziať späť. Are you sure you wish to continue? Naozaj chcete pokračovať? Apply Transformation button Aplikovať transformáciu Cancel Transformation button Zrušiť transformáciu F1 Help Shortcut TransformWindowBase Transform Model Transformovať model X Y Z Translate Posunúť Euler Angles Eulerove uhly Rotate Otočiť Quaternion Angle Uhol Axis Osi Scale Zmena mierky (bottom row is translation) (spodný riadok je posunutie) Apply Matrix Aplikovať maticu Matrix Matica Apply to: Aplikovať na: Entire Model and Animations Celý model a animácie Press F1 for help Stlač F1 pre pomoc Close Zatvoriť Selected (including animations) Zvolené (vrátanie animácií) Entire Model (including animations) Celý model (vrátane animácií) ValueWin F1 Help Shortcut ValueWinBase Value Window Okno hodnoty Value Hodnota Press F1 for help Stlač F1 pre pomoc Ok Cancel Zrušiť ViewWindow Some models are unsaved. Save before exiting? Niektoré modely nie sú uložené. Uložiť ich pred uložením? Press F1 for help using any window Stlač F1 pre pomoč ku ktorémukoľvek oknu Animations Animácie Properties Vlastnosti New File|New Nový Open File|Open Otvoriť... Save File|Save Uložiť Save As File|Save As Uložiť ako... Set Background Image... File|Set Background Image Nastaviť obrázok pozadia... Run Script... File|Run Script Spustiť skript... Recent Scripts File|Recent Script Nedávne skripty Merge... File|Merge Zlúčiť... Merge Animations... File|Merge Animations Zlúčiť animáciu... Recent Models File|Recent Models Nedávne modely Plugins... File|Plugins Pluginy... Close File|Close Zatvoriť Quit File|Quit Skončiť Hide Joints View|Hide Joints Schovať kĺby Draw Joint Lines View|Draw Joint Lines Kresliť čiary kĺbov Draw Joint Bones View|Draw Joint Bones Kresliť kĺby kostí Draw Texture Projections View|Draw Texture Projections Kresliť premietanie textúr Hide Texture Projections View|Hide Texture Projections Schovať premietanie textúr Use Red Error Texture View|Use Red Error Texture Použiť červenú chybnú textúru Use Blank Error Texture View|Use Blank Error Texture Použiť prázdnu chybnú textúru Render 3D Lines View|Render 3D Lines Kreslit 3D čiary Hide 3D Lines View|Hide 3D Lines Schovať 3D čiary Draw Back-facing Triangles View|Draw Back-facing Triangles Kresliť zadné plochy trojuholníkov Hide Back-facing Triangles View|Hide Back-facing Triangles Schovať zadné strany trojúholníkov Frame All View|Frame Všetky rámce Frame Selected View|Frame Zvolené rámce Show Properties View|Show Properties Ukázať parametre Render Options View|Render Options Parametre kreslenia 3D Wireframe View|3D 3D drôtený 3D Flat View|3D 3D plochý 3D Smooth View|3D 3D hladký 3D Texture View|3D 3D textúrovaný 3D Alpha Blend View|3D 3D priehľadný Canvas Wireframe View|Canvas 2D drôtený Canvas Flat View|Canvas 2D plochý Canvas Smooth View|Canvas 2D hladký Canvas Texture View|Canvas 2D textúra Canvas Alpha Blend View|Canvas 2D priehľadný 1 View View|Viewports 1 pohľad 1x2 View View|Viewports 1x2 pohľady 2x1 View View|Viewports 2x1 pohľady 2x2 View View|Viewports 2x2 pohľady 2x3 View View|Viewports 2x3 pohľady 3x2 View View|Viewports 3x2 pohľady 3x3 View View|Viewports 3x3 pohľady Viewport Settings... View|Viewport Settings Nastavenie výrezu... Grid Tools|Snap to Grid Mriežka Vertex Tools|Snap to Vertex Vrchol Undo Vrátiť spať Ctrl+Z Undo shortcut Redo Vrátiť zrušené Ctrl+Y Redo shortcut Snap To Prichytiť k Tools Nástroje Boolean Operation... Groups|Boolean Operation Booleovské operácie... Edit Model Meta Data... Groups|Edit Model Meta Data Upraviť metadáta modelu Transform Model... Groups|Transform Model Transformovať model... Edit Joints... Joints|Edit Joints Upraviť kĺby... Assign Selected to Joint Joints|Assign Selected to Joint Priradiť zvolené ku kĺbom Remove All Influences from Selected Joints|Remove All Influences from Selected Odstrániť všetky body pôsobenia z označených Remove Selected Joint from Influencing Joints|Remove Selected Joint from Influencing Odstrániť zvolené kĺby z bodov pôsobenia Convert Multiple Influences to Single Joints|Convert Multiple Influences to Single Konvertovať body pôsobenia do jedného Select Joint Influences Joints|Select Joint Influences Zvoliť kĺby pôsobenia Select Influenced Vertices Joints|Select Influenced Vertices Zvoliť vrcholy pôsobenia Select Influenced Points Joints|Select Influenced Points Zvoliť body pôsobenia Select Unassigned Vertices Joints|Select Unassigned Vertices Zvoliť nezvolené vrcholy Select Unassigned Points Joints|Select Unassigned Points Zvoliť nepriradené body Start Animation Mode... Animation|Start Animation Mode Spustiť režim animácie... Stop Animation Mode Animation|Stop Animation Mode Zastaviť režim animácie Animation Sets... Animation|Animation Sets Skupiny animácií... Export Animation... Animation|Export Animation Exportovať animáciu... Copy Animation Frame Animation|Copy Animation Frame Kopírovať rámec animácie Paste Animation Frame Animation|Paste Animation Frame Vložiť rámec animácie Clear Animation Frame Animation|Clear Animation Frame Vymazať rámec animácie Set Rotation Keyframe Animation|Set Rotation Keyframe Nastaviť kľúčový rámec otočenia Set Translation Keyframe Animation|Set Translation Keyframe Nastaviť kľúčový rámec posunutia Contents... Help|Contents Obsah... License... Help|License Licencia... About... Help|About O programe... &File menu bar &Súbor &View menu bar &Pohľad &Tools menu bar &Nástroje &Geometry menu bar Ú&tvary &Materials menu bar S&kupiny &Influences menu bar Pôso&benie &Animation menu bar &Animácia &Help menu bar Po&moc All Supported Formats ( Všetky podporované formáty ( ;; All Files (*) ;; Všetky súbory (*) All Files (*) Všetky súbory (*) Save model file as Uložiť model ako File exists. Overwrite? Súbor existuje. Prepísať? All Supported Formats ( model formats Všetky podporované formáty ( Open model file Otvoriť súbor modelu Merge models Zlúčiť modely : : Script %1 complete Skript %1 ukončený Script %1 error %2 Skript %1 chyba %2 Save first? Uložiť prvý? Model has been modified Do you want to save before closing? Model bol zmenený Chcete ho uložiť pred zatvorením? Unknown response: %1, Canceling close request Neznáma odpoveď: %1, Prerušujem zatvorenie Hide Properties View|Hide Properties Schovať vlastnosti Cannot hide with selected projections. Unselect projections now? Nemožno schovať so zvolenými projekciami. Odznačiť najprv projekcie? Hide projections Schovať premietanie Cannot hide with selected joints. Unselect joints now? Nemožno schovať so zvolenými kĺbmi, odznačiť najprv kĺby? Hide bone joints Schovať kĺby kostí You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'edit texture coordinates' window Najprv musíte zvoliť plochu. Použite náztroj 'Označiť plochu'. You must select faces first. Use the 'Select Faces' tool. Notice that user must have faces selected to open 'paint texture' window Najprv musíte zvoliť plochu. Použite náztroj 'Označiť plochu'. Undo %1 Späť %1 Nothing to undo Niet čo vrátiť späť Redo %1 Znova %1 Nothing to redo Nič sa nedá vrátiť späť This model does not have any animations Tento model neobsahuje žiadne animácie Set rotation keframe Nastaviť kĺúčový rámec otočenia Set translation keframe Nastaviť kĺučový rámec posunutia Unknown response: Canceling operation Neznáma odozva: Ruším operáciu [unnamed] For filename in title bar (if not set) [bezmena] Assigning %1 vertices and %2 points to joints Priraďujem %1 vrcholov a %2 bodov ku kĺbu Assign Selected to Joint Priradiť zvolené ku kĺbom You must have at least one bone joint selected. Musíte mať zvolený aspoň jeden kĺb. Remove All Influences from Selected Odstrániť všetky body pôsobenia z označených Remove Joint from Influencing Odstrániť kĺby z bodov pôsobenia Convert To Single Influence Konvertovať na jeden bod pôsobenia Select Unassigned Vertices Zvoliť nepriradené vrcholy Select Unassigned Points Zvoliť nepriradené body Select Joint Influences Zvoliť kĺby pôsobenia Select Influences Vertices Zvoliť vrcholy pôsobenia Select Influenced Points Zvoliť body pôsobenia Auto-Assign Selected... Joints|Auto-Assign Selected Automaticky priradiť zvolené... Auto-Assign Selected to Bone Joints Automaticky priradiť zvolené ku kĺbom You must have at least one vertex or point selected. Musíte mať zvolený najmenej jeden vrchol alebo bod. Open... File|Open Otvoriť... Save As... File|Save As Uložiť ako... Export... File|Export Export... Edit Model Meta Data... Model|Edit Model Meta Data Upraviť metadáta modelu... Transform Model... Model|Transform Model Transformovať model... Boolean Operation... Model|Boolean Operation Booleovské operácie... Set Background Image... Model|Set Background Image Nastaviť obrázok pozadia... Merge... Model|Merge Zlúčiť... Import Animations... Model|Import Animations Import animácií... Edit Groups... Materials|Edit Groups Editovať skupiny... Edit Materials... Materials|Edit Materials Editovať materiály... Clean Up... Materials|Clean Up Vyčistiť skupiny... Reload Textures Materials|Reload Textures Znovu načítať textúry Edit Projection... Materials|Edit Projection Editovať premietanie... Edit Texture Coordinates... Materials|Edit Texture Coordinates Editovať súradnice textúry... Paint Texture... Materials|Paint Texture Namaľovať textúru... Save Animation Images... Animation|Save Animation Images Uložiť obrázky animácií... Copy Selected Keyframes Animation|Copy Animation Frame Kopírovať zvolené kľúčové rámce Paste Selected Keyframes Animation|Paste Animation Frame Vložiť zvolené kľúčové rámce Clear Rotation Keyframe Animation|Clear Rotation Keyframe Clear Translation Keyframe Animation|Clear Translation Keyframe &Model menu bar &Model Mate&rials menu bar Mate&riály All Exportable Formats Všetky exportovateľné formáty All Writable Formats Všetky zapisovateľné formáty Export model Exportovať model All Supported Formats model formats Všetky podporované formáty Merge animations Clear rotation keframe Clear translation keframe All Supported Formats Všetky podporované formáty F1 Help Shortcut Export Selected... File|Export Selected Exportovať zvolené... You must have at least 1 face, joint, or point selected to Export Selected Musíte zvoliť aspoň 1 plochu alebo bod na Exportovanie zvoleného You are in animation mode, but there are no animations Ste v režime animácie, ale nie sú tu žiadne animácie ViewportSettings F1 Help Shortcut ViewportSettingsBase Viewport Settings Nastavenie výrezu Canvas Grid Mriežka plátna Default Grid Unit Predvolená jednotka mriežky 3D Grid 3D mriežka Grid Lines Čiary mriežky X/Y Plane Rovina X/Y X/Z Plane Rovina X/Z Y/Z Plane Rovina Y/Z Press F1 for help Stlač F1 pre pomoc &Ok &Ok Alt+O &Cancel &Zrušiť Alt+C Alt+Z Decimal Grid Desiatková mriežka Binary Grid Fixed Grid mm3d-1.3.15/util/000077500000000000000000000000001466047437300134345ustar00rootroot00000000000000mm3d-1.3.15/util/Makefile.am000066400000000000000000000003631466047437300154720ustar00rootroot00000000000000noinst_PROGRAMS = hpagemake hpagemake_SOURCES = hpagemake.c # When cross-compiling hpagemake needs to be run on the build host # to generate the documentation. CC = $(CC_FOR_BUILD) CFLAGS = $(CFLAGS_FOR_BUILD) LDFLAGS = $(LDFLAGS_FOR_BUILD) mm3d-1.3.15/util/hpagemake.c000066400000000000000000000304221466047437300155230ustar00rootroot00000000000000/* hpagemake.c * * Copyright (c) 2021 Zack Middleton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * See the COPYING file for full license text. */ // This is a C implementation of mm3d's util/hpagemake.pl that can be // used without installing Perl and HTTP::Template module. // It only implements a subset of HTTP::Template functionality needed // for MM3D documentation. #ifndef _WIN32 #define _POSIX_C_SOURCE 200809L #define _XOPEN_SOURCE 700 #endif #include #include #include #include #include #include #include #include // GNU Hurd doesn't define this. // TODO: Technically max path length should be dynamically allocated. #ifndef PATH_MAX #define PATH_MAX 4096 #endif #define LAZY_LOAD // Referenced files (e.g., SECLEFT= *bufferSize ) { *string = (char*)safe_realloc( *string, *length + addLength + 1 ); *bufferSize = *length + addLength + 1; } memcpy( &(*string)[*length], add, addLength ); (*string)[*length + addLength] = '\0'; *length += addLength; } char *ReadFile( const char *filename ) { FILE *f; f = fopen( filename, "rb" ); if ( !f ) { fprintf( stderr, "warning: failed to read file '%s'\n", filename ); return NULL; } fseek( f, 0, SEEK_END ); long length = ftell( f ); fseek( f, 0, SEEK_SET ); char *contents = (char*)safe_malloc( length + 1 ); if ( fread( contents, 1, length, f ) != (size_t)length ) { free( contents ); fclose( f ); fprintf( stderr, "warning: failed to read file '%s'\n", filename ); return NULL; } contents[length] = '\0'; fclose( f ); //fprintf( stderr, "debug: read file '%s'\n%s\n", filename, contents ); return contents; } strpair *StrPairFindKey( strpair *vars, char *key ) { if ( !vars ) { return NULL; } for ( strpair *var = vars; var->key; var++ ) { if ( strcasecmp( var->key, key ) == 0 ) { return var; } } return NULL; } void FreeStrPairs( strpair *vars ) { if ( !vars ) { return; } for ( strpair *var = vars; var->key; var++ ) { free( var->key ); free( var->value ); } free( vars ); } void StrPairResolveValue( strpair *var, const char *basepath, sublist *sub ) { if ( !var || var->value[0] != '<' || var->valueReadFromFile ) { return; } var->valueReadFromFile = 1; char filename[PATH_MAX]; snprintf( filename, sizeof( filename ), "%s/%s", basepath, &var->value[1] ); filename[sizeof(filename)-1] = '\0'; char *contents = ReadFile( filename ); if ( contents ) { char *output; if ( sub ) { TemplateReplace( basepath, contents, sub, NULL, &output ); free( contents ); } else { output = contents; } free( var->value ); var->value = output; } } strpair *StrPairsFromText( const char *contents ) { // Count the number of variables. const char *text = contents; int numVars = 0; while ( 1 ) { const char *start = text; const char *end = NULL; const char *split = NULL; if ( !*start ) { break; } for ( int i = 0; text[i] != '\0'; i++ ) { if ( text[i] == '=' ) { split = &text[i]; } if ( text[i] == '\r' || text[i] == '\n' ) { end = &text[i]; text = end + 1; break; } } if ( !split ) { continue; } numVars++; } // Allocate the variables. strpair *vars = (strpair *)safe_malloc( ( numVars + 1 ) * sizeof( strpair ) ); numVars = 0; text = contents; while ( 1 ) { const char *start = text; const char *end = NULL; const char *split = NULL; if ( !*start ) { break; } for ( int i = 0; text[i] != '\0'; i++ ) { if ( text[i] == '=' ) { split = &text[i]; } if ( text[i] == '\r' || text[i] == '\n' ) { end = &text[i]; text = end + 1; break; } } if ( !split ) { continue; } // Replace existing value for key. int varIndex = -1; for ( int i = 0; i < numVars; i++ ) { if ( strncasecmp( vars[i].key, start, split - start ) == 0 ) { varIndex = i; break; } } if ( varIndex != -1 ) { // Replace key if it's a different case so debug messages make more sense. if ( strncmp( vars[varIndex].key, start, split - start ) != 0 ) { free( vars[varIndex].key ); vars[varIndex].key = CopyText( start, split - start ); } free( vars[varIndex].value ); } else { varIndex = numVars; numVars++; vars[varIndex].key = CopyText( start, split - start ); } vars[varIndex].value = CopyText( split+1, end - (split+1) ); vars[varIndex].valueReadFromFile = 0; //fprintf( stderr, "var %d: key='%s', value='%s'\n", varIndex, vars[varIndex].key, vars[varIndex].value ); } vars[numVars].key = NULL; vars[numVars].value = NULL; vars[numVars].valueReadFromFile = 0; return vars; } strpair *StrPairsFromFile( const char *filename ) { char *contents = ReadFile( filename ); strpair *vars = StrPairsFromText( contents ); free( contents ); return vars; } void TemplateReplace( const char *basepath, const char *template_, sublist *sub1, sublist *sub2, char **output ) { const char *text = template_; size_t outputLength = 0; size_t outputBufferSize = 0; *output = NULL; while ( 1 ) { char *tmpl_start = my_strcasestr( text, "' ); if ( !tmpl_end ) { break; } tmpl_end++; char *name_start = my_strcasestr( tmpl_start + 1, "name=" ); if ( !name_start ) { fprintf( stderr, "warning: TMPL_VAR without name\n" ); // Skip in the output text. text = tmpl_end; continue; } name_start += strlen( "name=" ); int quote = 0; if ( name_start[0] == '\'' ) { name_start++; quote = 1; } if ( name_start[0] == '\"' ) { name_start++; quote = 2; } char *name_end = NULL; if ( quote == 2 ) { name_end = strchr( name_start, '\"' ); } else if ( quote == 1 ) { name_end = strchr( name_start, '\'' ); } else { name_end = strchr( name_start, ' ' ); } if ( !name_end || name_end >= tmpl_end ) { // Missing end quote. name_end = tmpl_end - 1; // if ( name_end[0] == '/' ) { name_end--; } } char *key = CopyText( name_start, name_end - name_start ); strpair *var = NULL; sublist *templatesub = NULL; if ( !var && sub1 ) { var = StrPairFindKey( sub1->vars, key ); if ( var ) { templatesub = sub1->templatesub; } } if ( !var && sub2 ) { var = StrPairFindKey( sub2->vars, key ); if ( var ) { templatesub = sub2->templatesub; } } if ( !var ) { fprintf( stderr, "warning: no defined value for TMPL_VAR name '%s'\n", key ); //AppendText( output, &outputLength, &outputBufferSize, text, tmpl_end - text ); // Skip in the output text. AppendText( output, &outputLength, &outputBufferSize, text, tmpl_start - text ); } else { StrPairResolveValue( var, basepath, templatesub ); //fprintf( stderr, "debug: replaced '%.*s' with '%s' (key '%s')\n", tmpl_end - tmpl_start, tmpl_start, value, key ); AppendText( output, &outputLength, &outputBufferSize, text, tmpl_start - text ); if ( var ) { AppendText( output, &outputLength, &outputBufferSize, var->value, strlen( var->value ) ); } } text = tmpl_end; free( key ); } AppendText( output, &outputLength, &outputBufferSize, text, strlen( text ) ); } int main( int argc, char **argv ) { time_t sec = time( NULL ); struct tm localtm; memcpy( &localtm, localtime( &sec ), sizeof( struct tm ) ); // Mimic the output of `LC_ALL=C date` on Linux. // Sat Aug 1 08:56:58 PDT 2009 // (Though on Windows it uses non-abbreviated timezone name.) const char *days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; const char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char timestamp[128]; tzset(); snprintf( timestamp, sizeof( timestamp ), "%s %s %d %d:%02d:%02d %s %d", days[localtm.tm_wday], months[localtm.tm_mon], localtm.tm_mday, localtm.tm_hour, localtm.tm_min, localtm.tm_sec, tzname[localtm.tm_isdst], localtm.tm_year + 1900 ); timestamp[sizeof(timestamp)-1] = '\0'; int date_year = localtm.tm_year + 1900; char commonpage[1024]; snprintf( commonpage, sizeof( commonpage ), "SECLEFT=\n" "\n" "hpagemake reads a file containing key value pairs:\n" "\n" "PAGE_NAME=The Page Name\n" "PAGE_CONTENT=\n" "<TMPL_VAR name=\"PAGE_NAME\"/>\n" "\n" "\n" "\n" "and print it to standard out.\n" "\n" "The following built-in variables exist which can be used in both the\n" "template and files referenced by the .page file:\n" "\n" "%s", commonpage ); return EXIT_SUCCESS; } for ( int i = 1; i < argc; i++ ) { char *argv_i_copy = CopyText( argv[i], strlen( argv[i] ) ); char *basepath = dirname( argv_i_copy ); sublist commonsub; commonsub.vars = StrPairsFromText( commonpage ); commonsub.templatesub = &commonsub; #ifndef LAZY_LOAD if ( commonsub.vars ) { for ( strpair *var = commonsub.vars; var->key; var++ ) { StrPairResolveValue( var, basepath, commonsub.templatesub ); } } #endif sublist pagesub; pagesub.vars = StrPairsFromFile( argv[i] ); pagesub.templatesub = &commonsub; #ifndef LAZY_LOAD if ( pagesub.vars ) { for ( strpair *var = pagesub.vars; var->key; var++ ) { StrPairResolveValue( var, basepath, pagesub.templatesub ); } } #endif char filename[PATH_MAX]; snprintf( filename, sizeof( filename ), "%s/%s", basepath, "template.htm" ); filename[sizeof(filename)-1] = '\0'; char *contents = ReadFile( filename ); if ( contents ) { char *output; TemplateReplace( basepath, contents, &pagesub, &commonsub, &output ); fprintf( stdout, "%s", output ); free( output ); free( contents ); } FreeStrPairs( pagesub.vars ); FreeStrPairs( commonsub.vars ); free( argv_i_copy ); } return EXIT_SUCCESS; } mm3d-1.3.15/util/hpagemake.pl000077500000000000000000000040261466047437300157200ustar00rootroot00000000000000#!/usr/bin/perl -w # # NOTE: This file (hpagemake.pl) was replaced with hpagemake.c in Maverick Model 3D. # # How to install HTML::Template: # # Debian/Ubuntu: # sudo apt install libhtml-template-perl # Fedora: # sudo dnf install perl-HTML-Template # General: # cpan App::cpanminus # choose sudo in the interactive command # cpanm HTML::Template --sudo # use HTML::Template; use File::Basename; my $srcdir = dirname(__FILE__).'/../doc/html'; foreach my $file ( @ARGV ) { if ( open( INFILE, $file ) ) { my %vars = (); my @lt = localtime(); $vars{ 'SECLEFT' } = `cat "$srcdir/secleft.htm"`; $vars{ 'SECRIGHT' } = `cat "$srcdir/secright.htm"`; $vars{ 'SECEND' } = `cat "$srcdir/secend.htm"`; $vars{ 'TIMESTAMP' } = `date`; $vars{ 'DATE_YEAR' } = $lt[5] + 1900; while ( ) { if ( /^(.*)=(.*)$/ ) { my $key = $1; my $value = $2; $key =~ s/^\s+//g; $key =~ s/\s+$//g; $value =~ s/^\s+//g; $value =~ s/\s+$//g; if ( $value =~ /^<(.*)$/ ) { my $content_file = $1; my $text = `cat "$srcdir/$content_file"`; my %vars = (); $vars{ 'SECLEFT' } = `cat "$srcdir/secleft.htm"`; $vars{ 'SECRIGHT' } = `cat "$srcdir/secright.htm"`; $vars{ 'SECEND' } = `cat "$srcdir/secend.htm"`; $vars{ 'TIMESTAMP' } = `date`; $vars{ 'DATE_YEAR' } = $lt[5] + 1900; my $t = HTML::Template->new_array_ref( [ $text ], die_on_bad_params => 0 ); $t->param( %vars ); $value = $t->output; } $vars{ $key } = $value; } } close( INFILE ); my $template = HTML::Template->new( filename => "$srcdir/template.htm", die_on_bad_params => 0 ); $template->param( %vars ); print $template->output; } else { die "$file: $!\n"; } }