Official MeshMagick thread - now licensed under MIT

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
Post Reply
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Post by haffax »

Just add the directory where the meshmagick.exe is in to your PATH variable.
How to do this is explained here: http://www.computerhope.com/issues/ch000549.htm

And here even a video tutorial ^^
http://showmedo.com/videos/video?name=9 ... eriesID=96
team-pantheon programmer
creators of Rastullahs Lockenpracht
compvis
Gremlin
Posts: 165
Joined: Wed Sep 10, 2008 6:14 am

Post by compvis »

thank you !

I can do anything with the tool !
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39
Contact:

Post by Beauty »

In the wiki I see the current version 0.5, but meshmagick.exe doesn't tell its version number.
It would be nice if you can add this in further versions.
Also don't forget my wish about sum of all verticed+triangles (see here)
It's still not in the TODO file of the SVN.

If there is an important update, please also update the wiki page (if needed).
A single download file (as alternative to the OgreCommandlineTools) would be nice.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Post by haffax »

The version number is shown by entering.

Code: Select all

meshmagick -version
Also don't forget my wish about sum of all verticed+triangles (see here)
It's still not in the TODO file of the SVN.
It's not in the TODO list, because it is done already. (see here)
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39
Contact:

Post by Beauty »

oh sorry, I didn't see your entry.

The option "-version" will not be displayed when you call meshmagick.
You could add the version number to the 2 status lines:
MeshMagick 0.5 - versatile Ogre mesh manipulation tool.
Copyright 2008 by Daniel Wickert

or
MeshMagick - versatile Ogre mesh manipulation tool.
Version 0.5 Copyright 2008 by Daniel Wickert


By the way - if a parameter is unknown (or mistyped) the application crashes
e.g. meshmagick -crash

This is related to version 0.4 ... I should update my meshmagick file ...
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
different
Gnoblar
Posts: 5
Joined: Fri Jul 18, 2008 8:49 am

Post by different »

Sorry for the stupid question but: where can I find version 0.5? I downloaded Command Line Tools but they include version 0.4?

And I have another problem. When trying to align a mesh, meshmagick is unable to access the skeleton file. Do you know why? Is there a way to avoid this error?
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Post by haffax »

different, there is no binary release for meshmagick 0.5 right now but you can get it from the ogreaddons project site (See ogre homepage), but the difference is only a few bugfixes. One fix could be related to your problem though.

But: What exactly was the meshmagic call you used, what exactly was the output of meshmagick and where were mesh and skeleton files located in your case?

Beauty, your meshmagick version has to be really old, the crash bug has been fixed for a long time.
team-pantheon programmer
creators of Rastullahs Lockenpracht
different
Gnoblar
Posts: 5
Joined: Fri Jul 18, 2008 8:49 am

Post by different »

haffax wrote:different, there is no binary release for meshmagick 0.5 right now but you can get it from the ogreaddons project site (See ogre homepage), but the difference is only a few bugfixes. One fix could be related to your problem though.

But: What exactly was the meshmagic call you used, what exactly was the output of meshmagick and where were mesh and skeleton files located in your case?
I used the call

Code: Select all

meshmagick transform -yalign=bottom input.mesh -- ouput.mesh
the result I got was that the mesh was correctly aligned while the skeleton
was not modified, because MeshMagick could not access it. Meanwhile I solved the problem of skeleton file access copying meshmagick.exe and the OGRE dll to the folder where the mesh is.
Is there another way to solve it?
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Post by haffax »

This bug is resolved in 0.5. skeleton is now found too by the transform tool if the to be transformed mesh is not in the working directory.

You might want to put meshmagick somewhere in your path. I don't know how the binary in the command line tools is built, but the source build should create astatically linked exe and thus the ogre_main.dll is not needed.
Might be different for the one from ogre command line tools.
team-pantheon programmer
creators of Rastullahs Lockenpracht
atorkhov
Gnoblar
Posts: 24
Joined: Sat Sep 13, 2008 5:29 pm
Location: Moscow, Russia

Re: [New Tool] Ogre MeshMagick - First official release

Post by atorkhov »

I've made patch for help building MeshMagick under linux some time ago, but seems that it still wasn't applied.
Patch fixes following problems:
  • Includes missing headers to tarball produced by make dist.
  • Fixes build in case if wasn't ran from top source dir.
  • Fixes dependency problems with multijobs-make build.
http://atorkhov.fedorapeople.org/meshmagick-0.4.0.patch
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

Thanks atorkhov, I am developing on Linux now and am currently fixing build issues. I started using cmake and when it is complete, I will remove all autotools build files, as I don't want to support too many build systems. CMake, because it is easier (for me at least) to write build scripts and because of its flexibility regarding out-of-source builds.
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

Ok, as far as I am concerned last cmake issues for linux are resolved. Packaging and installation now work too.
I wait with autotools file removal until I have feedback regarding linux build. So please test it. :)
team-pantheon programmer
creators of Rastullahs Lockenpracht
atorkhov
Gnoblar
Posts: 24
Joined: Sat Sep 13, 2008 5:29 pm
Location: Moscow, Russia

Re: [New Tool] Ogre MeshMagick - First official release

Post by atorkhov »

I don't know cmake well, but make package is failing:

Code: Select all

$ make package
[ 95%] Built target meshmagick_shared_lib
[100%] Built target meshmagick_bin
Run CPack packaging tool...
CPack: Create package using STGZ
CPack: Install projects
CPack: - Run preinstall target for: meshmagick
CPack: - Install project: meshmagick
CMake Error at cmake_install.cmake:70 (FILE):
  file INSTALL cannot copy file
  "/home/alex/Develop/3d/ogreaddons/trunk/meshmagick/meshmagick.pc" to
  "/usr/local/lib/pkgconfig/meshmagick.pc".


CMake Error at cmake_install.cmake:74 (FILE):
  file cannot create directory: /usr/local/include/meshmagick.  Maybe need
  administrative privileges.


CPack Error: Error when generating package: meshmagick
make: *** [package] Error 1
make package_source is also working weird:
First, it creates packages with version 0.1.1 not 0.5.1. Next, only good package is gzip.
In bzip2 package it includes directory _CPack_Packages which contains a copy of all files and .tar.gz package, also it has same .tar.gz package in root of archive. In tar.Z package it includes directory _CPack_Packages with dirs for .tar.bz2 and .tar.gz, and a lots of copies of those packages and individual files.
This result in big size of .bz2 - 4M and .Z - 25M ! While all source dir with compiled binaries and .svn is only about 5M

Cmake version 2.6.2. Looks like it more buggy than autotools :)

By the way, it's possible to do "out-of-source" build with autotools too - just run configure from desired directory.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

Thank you very much for your feedback, atorkhov-

The first problem with the binary packages might be due to missing rights to write to /usr/local/lib? Packaging uses install in cmake and you need the same rights as you'd need when you do make install.

For the others, I haven't checked source packaging, will have a look. CPack is widely configurable. So I am confident this is all solvable, but you are right it sounds as if some of the things are simply bugs. Anyway, will look at this later this week.
team-pantheon programmer
creators of Rastullahs Lockenpracht
atorkhov
Gnoblar
Posts: 24
Joined: Sat Sep 13, 2008 5:29 pm
Location: Moscow, Russia

Re: [New Tool] Ogre MeshMagick - First official release

Post by atorkhov »

haffax wrote:The first problem with the binary packages might be due to missing rights to write to /usr/local/lib? Packaging uses install in cmake and you need the same rights as you'd need when you do make install.
Sure, I don't have rights - I don't want to install it in /usr/local - "make install DESTDIR=~/tmp/meshmagick" works perfectly.

Anyway, if make package run by root it composes some archives. Let's expore them:

Code: Select all

$ tar tjf meshmagick-0.1.1-Linux.tar.bz2
meshmagick-0.1.1-Linux/lib/libmeshmagick.so
meshmagick-0.1.1-Linux/lib/libmeshmagick.so.0.5
meshmagick-0.1.1-Linux/lib/libmeshmagick.so.0.5.1
meshmagick-0.1.1-Linux/bin/meshmagick
But where are includes and pkgconfig file? Hm, it installed it in /usr/local. It didn't install /usr/local/bin/meshmagick though.
So, something went wrong - some files were packages and some installed.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

Hmm, don't remember this problem. I will have a look, thanks. :)
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

@atorkhov, today I applied your patch to svn. Thank you very much for it.
Getting packaging to work with cmake is harder than I initially thought. Right now I have other items I'd like to work at, so till everything is resolved, autotools build stays the canonical Linux build env.

I will later decide whether cmake is worth it or not. My initial hope was that with cmake I can maintain a singe build system for all platforms. So far I am not content with the code::blocks project it creates. My hand made one is much better organized, VC and XCode generation are so far not tested. Building is nothing I want to invest much time on, instead I rather use my time to improve the program itself. :)
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

@glennr, better late then never, I just applied your patch regarding the merge of vertex animations in the merge tool. Thanks for it. :)
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

Almost half a year after submission, I applied the patch by hagish with a few changes. Thank you very much for it.
Hagish' patch introduced vertex winding change. There now is the transform tool option -flip-vertex-winding, which changes vertex winding on the mesh. This works well in conjunction with negative scaling.

For instance, you can now do

Code: Select all

meshmagick transform -flip-vertex-winding -scale=1/-1/1 mymesh.mesh
to turn a mesh upside-down.

Next step is to introduce auto flipping of vertex winding, so that you just do the transform and the transform tool determines whether vertex winding has to be changed. This will come together with the new -axes transform option, which lets you map the x/y/z axes to another set of axes like z/-y/x. Makes some transforms more intuitive instead of using potentially negative scaling and rotation (which is equivalent.)
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

As announced, there now is a new option for the transform tool. The axes-option allows to remap main axes. This is useful when you get meshes out of a modeller in a different coordinate system than you expect for your project. The axes-option doesn't add new functionality, as you already could do this using rotation and negative scaling, but it is more convenient than figuring out the right rotation and scale.

Example: To turn a mesh upside-down, you can write:

Code: Select all

meshmagick transform -axes=x/-y/z mymesh.mesh
The transform tool now automatically detects whether vertex winding has to be changed due to negative scaling or axes transformation.
team-pantheon programmer
creators of Rastullahs Lockenpracht
juliusctw
Gremlin
Posts: 159
Joined: Thu Sep 28, 2006 8:15 pm

gui version

Post by juliusctw »

Hello I started writing a gui front end for meshmagick, it is written wxpython and so far it seems to work in linux and windows, you just have to put this program in the same folder as Meshmagick.exe and it should work in windows. In linux you would have to have meshmagick already installed.

please help me test it , and let me know if there are any other features you would like added . I'm thinking of handling , ogreXMLconverter and the mesh upgrade, anyway, here is the source code, you would need wxpython to run it.

Code: Select all

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# generated by wxGlade 0.6.3 on Thu Feb 26 15:04:43 2009

import sys
import os


try:
	import wx
except:
	if os.name == "posix":
		r = os.system('zenity --error --text="Cannot run iserverGUI\nyou must first install wxpython"')
		if r != 0:
			r = os.system('kdialog --error "Cannot run iserverGUI\nYou must first install wxpython"')

# begin wxGlade: extracode
# end wxGlade

def progExist(ProgName):
	paths = os.environ["PATH"]
	binDirs = paths.split(":")
	
	allProg = []
	
	for each in binDirs:
		bin = os.listdir(each)	
		allProg = allProg + bin
	
	if ProgName in allProg: return True
	return False



class InfoDialog(wx.Dialog):
	def __init__(self, *args, **kwds):
		# begin wxGlade: InfoDialog.__init__
		kwds["style"] = wx.DEFAULT_DIALOG_STYLE
		wx.Dialog.__init__(self, *args, **kwds)
		self.label_2 = wx.StaticText(self, -1, "You must kill the iserver and then restart for this feature to take effect", style=wx.ALIGN_CENTRE)
		self.ExitInfoButton = wx.Button(self, -1, "ok")

		self.__set_properties()
		self.__do_layout()

		self.Bind(wx.EVT_BUTTON, self.onExitInfo, self.ExitInfoButton)
		# end wxGlade

	def __set_properties(self):
		# begin wxGlade: InfoDialog.__set_properties
		self.SetTitle("Info")
		self.SetSize((250, 151))
		# end wxGlade

	def __do_layout(self):
		# begin wxGlade: InfoDialog.__do_layout
		sizer_7 = wx.BoxSizer(wx.VERTICAL)
		sizer_7.Add(self.label_2, 2, wx.TOP|wx.EXPAND, 12)
		sizer_7.Add(self.ExitInfoButton, 1, wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, 19)
		self.SetSizer(sizer_7)
		self.Layout()
		# end wxGlade

	def onExitInfo(self, event): # wxGlade: InfoDialog.<event_handler>
		print "Event handler `onExitInfo' not implemented!"
		event.Skip()

# end of class InfoDialog


class MyFrame(wx.Frame):
	def __init__(self, *args, **kwds):
		# begin wxGlade: MyFrame.__init__
		kwds["style"] = wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.MAXIMIZE_BOX|wx.SYSTEM_MENU|wx.RESIZE_BORDER|wx.FULL_REPAINT_ON_RESIZE|wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN
		wx.Frame.__init__(self, *args, **kwds)
		self.notebook_1 = wx.Notebook(self, -1, style=0)
		self.panel_1 = wx.Panel(self.notebook_1, -1)
		self.OptimizePanel = wx.Panel(self.notebook_1, -1)
		
		# Menu Bar
		self.frame_1_menubar = wx.MenuBar()
		self.FileMenu = wx.Menu()
		self.FileMenu.AppendSeparator()
		self.quit = wx.MenuItem(self.FileMenu, wx.NewId(), "Quit", "", wx.ITEM_NORMAL)
		self.FileMenu.AppendItem(self.quit)
		self.frame_1_menubar.Append(self.FileMenu, "&File")
		wxglade_tmp_menu = wx.Menu()
		wxglade_tmp_menu.Append(wx.NewId(), "About", "", wx.ITEM_NORMAL)
		self.frame_1_menubar.Append(wxglade_tmp_menu, "&About")
		self.SetMenuBar(self.frame_1_menubar)
		# Menu Bar end
		self.meshFile = wx.StaticText(self.OptimizePanel, -1, "mesh file", style=wx.ALIGN_CENTRE)
		self.pickMeshButton = wx.Button(self.OptimizePanel, -1, "pick mesh file")
		self.meshFilePathBox = wx.TextCtrl(self.OptimizePanel, -1, "", style=wx.TE_READONLY)
		self.static_line_1_copy_copy_1 = wx.StaticLine(self.OptimizePanel, -1)
		self.static_line_2 = wx.StaticLine(self.OptimizePanel, -1)
		self.meshFile_copy_copy = wx.StaticText(self.OptimizePanel, -1, "Renaming", style=wx.ALIGN_CENTRE)
		self.tolerance_copy_2 = wx.StaticText(self.OptimizePanel, -1, "-animation")
		self.animationBefore = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.animationAfter = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.static_line_3 = wx.StaticLine(self.OptimizePanel, -1, style=wx.LI_VERTICAL)
		self.tolerance_copy_2_copy = wx.StaticText(self.OptimizePanel, -1, "-bone     ")
		self.boneBefore = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.boneAfter = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.tolerance_copy_2_copy_1 = wx.StaticText(self.OptimizePanel, -1, "-material")
		self.materialBefore = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.materialAfter = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.static_line_3_copy = wx.StaticLine(self.OptimizePanel, -1, style=wx.LI_VERTICAL)
		self.tolerance_copy_2_copy_copy = wx.StaticText(self.OptimizePanel, -1, "-skeleton")
		self.skeletonRenamed = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.static_line_3_copy_1 = wx.StaticLine(self.OptimizePanel, -1, style=wx.LI_VERTICAL)
		self.tolerance_copy_2_copy_copy_copy = wx.StaticText(self.OptimizePanel, -1, "-submesh")
		self.submeshBefore = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.submeshAfter = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.static_line_2_copy = wx.StaticLine(self.OptimizePanel, -1)
		self.meshFile_copy_copy_copy = wx.StaticText(self.OptimizePanel, -1, "Transform", style=wx.ALIGN_CENTRE)
		self.scaleX = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.scaleY = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.scaleZ = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.tolerance_copy_2_copy_copy_1 = wx.StaticText(self.OptimizePanel, -1, "-resize")
		self.resizeX = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.resizeY = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.resizeZ = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.axesX = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.axesY = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.axesZ = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.tolerance_copy_2_copy_copy_1_copy = wx.StaticText(self.OptimizePanel, -1, "-translate")
		self.translateX = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.translateY = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.translateZ = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.rotate = wx.TextCtrl(self.OptimizePanel, -1, "")
		self.xaxis = wx.CheckBox(self.OptimizePanel, -1, "x axis")
		self.yaxis = wx.CheckBox(self.OptimizePanel, -1, "y axis")
		self.zaxis = wx.CheckBox(self.OptimizePanel, -1, "z axis")
		self.clearChanges = wx.Button(self.OptimizePanel, -1, "clear Changes ")
		self.commitChanges = wx.Button(self.OptimizePanel, -1, "commit changes")
		self.optimizeMeshButton = wx.Button(self.OptimizePanel, -1, "Optimize Mesh")
		self.meshFile_copy_1 = wx.StaticText(self.panel_1, -1, "Command output", style=wx.ALIGN_CENTRE)
		self.OutputBox = wx.TextCtrl(self.panel_1, -1, "", style=wx.TE_PROCESS_TAB|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_WORDWRAP)

		self.__set_properties()
		self.__do_layout()

		self.Bind(wx.EVT_MENU, self.onQuit, self.quit)
		self.Bind(wx.EVT_MENU, self.onAbout, id=-1)
		self.Bind(wx.EVT_BUTTON, self.onMediaDir, self.pickMeshButton)
		# end wxGlade

		self.Bind(wx.EVT_BUTTON, self.OnOptimizeMesh , self.optimizeMeshButton)
		self.Bind(wx.EVT_BUTTON, self.OnclearChanges, self.clearChanges)
		self.Bind(wx.EVT_BUTTON, self.OncommitChanges, self.commitChanges)

		self.xaxis.SetValue(True)
		self.Bind(wx.EVT_CHECKBOX, self.onChangeX, self.xaxis)
		self.Bind(wx.EVT_CHECKBOX, self.onChangeY, self.yaxis)
		self.Bind(wx.EVT_CHECKBOX, self.onChangeZ, self.zaxis)
		
		self.progL = "C:\OgreCommandLineTools\MeshMagick.exe"

	def __set_properties(self):
		# begin wxGlade: MyFrame.__set_properties
		self.SetTitle("meshmagic gui")
		self.SetSize((775, 600))
		self.meshFile.SetFont(wx.Font(16, wx.ROMAN, wx.NORMAL, wx.BOLD, 0, ""))
		self.meshFile_copy_copy.SetFont(wx.Font(16, wx.ROMAN, wx.NORMAL, wx.BOLD, 0, ""))
		self.meshFile_copy_copy_copy.SetFont(wx.Font(16, wx.ROMAN, wx.NORMAL, wx.BOLD, 0, ""))
		self.clearChanges.SetMinSize((200, 68))
		self.commitChanges.SetMinSize((200, 68))
		self.optimizeMeshButton.SetMinSize((200, 68))
		self.meshFile_copy_1.SetFont(wx.Font(16, wx.ROMAN, wx.NORMAL, wx.BOLD, 0, ""))
		# end wxGlade

	def __do_layout(self):
		# begin wxGlade: MyFrame.__do_layout
		sizer_1 = wx.BoxSizer(wx.VERTICAL)
		sizer_8 = wx.BoxSizer(wx.VERTICAL)
		sizer_5_copy_copy_1 = wx.BoxSizer(wx.VERTICAL)
		sizer_6 = wx.BoxSizer(wx.HORIZONTAL)
		sizer_5_copy_2_copy_copy = wx.BoxSizer(wx.HORIZONTAL)
		sizer_5_copy_2_copy = wx.BoxSizer(wx.HORIZONTAL)
		sizer_5_copy_2 = wx.BoxSizer(wx.HORIZONTAL)
		sizer_5_copy_1 = wx.BoxSizer(wx.HORIZONTAL)
		sizer_5 = wx.BoxSizer(wx.HORIZONTAL)
		sizer_6_copy_1_copy_1 = wx.BoxSizer(wx.HORIZONTAL)
		sizer_5_copy_copy_1.Add(self.meshFile, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 21)
		sizer_6_copy_1_copy_1.Add(self.pickMeshButton, 1, wx.LEFT|wx.RIGHT, 3)
		sizer_6_copy_1_copy_1.Add(self.meshFilePathBox, 3, wx.LEFT|wx.RIGHT, 4)
		sizer_5_copy_copy_1.Add(sizer_6_copy_1_copy_1, 0, wx.TOP|wx.EXPAND, 9)
		sizer_5_copy_copy_1.Add(self.static_line_1_copy_copy_1, 0, wx.TOP|wx.BOTTOM|wx.EXPAND, 15)
		sizer_5_copy_copy_1.Add(self.static_line_2, 0, wx.TOP|wx.BOTTOM|wx.EXPAND, 15)
		sizer_5_copy_copy_1.Add(self.meshFile_copy_copy, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 7)
		sizer_5.Add(self.tolerance_copy_2, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5.Add(self.animationBefore, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5.Add(self.animationAfter, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5.Add(self.static_line_3, 0, wx.LEFT|wx.RIGHT|wx.EXPAND, 10)
		sizer_5.Add(self.tolerance_copy_2_copy, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5.Add(self.boneBefore, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5.Add(self.boneAfter, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_copy_1.Add(sizer_5, 0, wx.EXPAND, 5)
		sizer_5_copy_1.Add(self.tolerance_copy_2_copy_1, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.materialBefore, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.materialAfter, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.static_line_3_copy, 0, wx.LEFT|wx.RIGHT|wx.EXPAND, 10)
		sizer_5_copy_1.Add(self.tolerance_copy_2_copy_copy, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.skeletonRenamed, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.static_line_3_copy_1, 0, wx.LEFT|wx.RIGHT|wx.EXPAND, 10)
		sizer_5_copy_1.Add(self.tolerance_copy_2_copy_copy_copy, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.submeshBefore, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_1.Add(self.submeshAfter, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_copy_1.Add(sizer_5_copy_1, 0, wx.EXPAND, 0)
		sizer_5_copy_copy_1.Add(self.static_line_2_copy, 0, wx.TOP|wx.BOTTOM|wx.EXPAND, 15)
		sizer_5_copy_copy_1.Add(self.meshFile_copy_copy_copy, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 7)
		tolerance_copy_2_copy_2 = wx.StaticText(self.OptimizePanel, -1, "-scale")
		sizer_5_copy_2.Add(tolerance_copy_2_copy_2, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.scaleX, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.scaleY, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.scaleZ, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.tolerance_copy_2_copy_copy_1, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.resizeX, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.resizeY, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2.Add(self.resizeZ, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_copy_1.Add(sizer_5_copy_2, 0, wx.EXPAND, 5)
		tolerance_copy_2_copy_2_copy = wx.StaticText(self.OptimizePanel, -1, "-axes")
		sizer_5_copy_2_copy.Add(tolerance_copy_2_copy_2_copy, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.axesX, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.axesY, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.axesZ, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.tolerance_copy_2_copy_copy_1_copy, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.translateX, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.translateY, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy.Add(self.translateZ, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_copy_1.Add(sizer_5_copy_2_copy, 0, wx.EXPAND, 5)
		tolerance_copy_2_copy_2_copy_copy = wx.StaticText(self.OptimizePanel, -1, "-rotate")
		sizer_5_copy_2_copy_copy.Add(tolerance_copy_2_copy_2_copy_copy, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy_copy.Add(self.rotate, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, 10)
		sizer_5_copy_2_copy_copy.Add(self.xaxis, 0, wx.LEFT|wx.RIGHT, 8)
		sizer_5_copy_2_copy_copy.Add(self.yaxis, 0, wx.LEFT|wx.RIGHT, 8)
		sizer_5_copy_2_copy_copy.Add(self.zaxis, 0, wx.LEFT|wx.RIGHT, 8)
		sizer_5_copy_copy_1.Add(sizer_5_copy_2_copy_copy, 0, wx.EXPAND, 5)
		sizer_6.Add(self.clearChanges, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 17)
		sizer_6.Add(self.commitChanges, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 26)
		sizer_6.Add(self.optimizeMeshButton, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 26)
		sizer_5_copy_copy_1.Add(sizer_6, 1, wx.EXPAND, 0)
		self.OptimizePanel.SetSizer(sizer_5_copy_copy_1)
		sizer_8.Add(self.meshFile_copy_1, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 21)
		sizer_8.Add(self.OutputBox, 1, wx.ALL|wx.EXPAND, 10)
		self.panel_1.SetSizer(sizer_8)
		self.notebook_1.AddPage(self.OptimizePanel, "Commit Changes")
		self.notebook_1.AddPage(self.panel_1, "Output")
		sizer_1.Add(self.notebook_1, 1, wx.EXPAND, 0)
		self.SetSizer(sizer_1)
		self.Layout()
		self.Centre()
		# end wxGlade

	def onQuit(self, event): # wxGlade: MyFrame.<event_handler>
		self.Close()
		sys.exit(1)

	def isNum(self, stri):
		try:
			float(stri)
			return True
		except: return False

	def onChangeX(self, event):
		if self.xaxis.GetValue():
			self.yaxis.SetValue(False)
			self.zaxis.SetValue(False)

	def onChangeY(self, event):
		if self.yaxis.GetValue():
			self.xaxis.SetValue(False)
			self.zaxis.SetValue(False)


	def onChangeZ(self, event):
		if self.zaxis.GetValue():
			self.xaxis.SetValue(False)
			self.yaxis.SetValue(False)


	def OncommitChanges(self, event):
		pathStr = self.meshFilePathBox.GetValue()
		if pathStr == "":
			wx.MessageBox('You must select a mesh file', 'Error')
			return

		self.checkRotation(pathStr)
		self.checkScale(pathStr)
		self.checkAxes(pathStr)
		self.checkResize(pathStr)
		self.checkTranslate(pathStr)

		self.checkAnimation(pathStr)
		self.checkMaterial(pathStr)
		self.checkSubMesh(pathStr)
		self.checkBone(pathStr)


	def runCommand(self, cmd):		
		self.OutputBox.AppendText(cmd)
		result = os.popen(cmd).read()
		self.OutputBox.AppendText(result)
		print cmd

	def checkTranslate(self, pth):
		x = self.translateX.GetValue()
		y = self.translateY.GetValue()
		z = self.translateZ.GetValue()

		if x == "" or y == "" or z == "": 
			return

		if not self.isNum(x) or not self.isNum(y) or not self.isNum(z): 
			return

		if os.name == "posix":
			cmd = "meshmagick transform -translate=" + x + "/" + y + "/" + z + " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " transform -translate=" + x + "/" + y + "/" + z + " " + pth

		self.runCommand(cmd)



	def checkResize(self, pth):
		x = self.resizeX.GetValue()
		y = self.resizeY.GetValue()
		z = self.resizeZ.GetValue()

		if x == "" or y == "" or z == "": 
			return

		if not self.isNum(x) or not self.isNum(y) or not self.isNum(z): 
			return


		if os.name == "posix":
			cmd = "meshmagick transform -resize=" + x + "/" + y + "/" + z + " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " transform -resize=" + x + "/" + y + "/" + z + " " + pth


		self.runCommand(cmd)

	def checkAxes(self, pth):
		x = self.axesX.GetValue()
		y = self.axesY.GetValue()
		z = self.axesZ.GetValue()

		if x == "" or y == "" or z == "": 
			return
		if not self.isNum(x) or not self.isNum(y) or not self.isNum(z): 
			return


		if os.name == "posix":
			cmd = "meshmagick transform -axes=" + x + "/" + y + "/" + z + " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " transform -axes=" + x + "/" + y + "/" + z + " " + pth


		self.runCommand(cmd)


	def checkScale(self, pth):
		x = self.scaleX.GetValue()
		y = self.scaleY.GetValue()
		z = self.scaleZ.GetValue()

		if x == "" or y == "" or z == "":
			return
		if not self.isNum(x) or not self.isNum(y) or not self.isNum(z):
			return


		if os.name == "posix":
			cmd = "meshmagick transform -scale=" + x + "/" + y + "/" + z + " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " transform -scale=" + x + "/" + y + "/" + z + " " + pth

		self.runCommand(cmd)



	def checkRotation(self, pth):
		rotV = self.rotate.GetValue()
		if rotV == "":
			return

		if not self.isNum(rotV):
			wx.MessageBox('Your rotational value must be a number', 'Error')
			return

		rotS = "/0/0/0 "

		if self.xaxis.GetValue():
			rotS = "/1/0/0 "
		elif self.yaxis.GetValue():
			rotS = "/0/1/0 "
		elif self.zaxis.GetValue():
			rotS = "/0/0/1 "



		if os.name == "posix":
			cmd = "meshmagick transform -rotate=" + rotV + rotS + pth + "\n"
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " transform -rotate=" + rotV + rotS + pth 

		self.runCommand(cmd)

	def getFileName(self, pth):
		os.chdir( os.path.split(pth)[0] )
		return os.path.split(pth)[1] 


	def checkBone(self, pth):
		after = self.boneAfter.GetValue()
		before = self.boneBefore.GetValue()

		if after == "" or before == "":
			return

		if os.path.splitext(  os.path.split(pth)[1] )[1] != ".skeleton": 
			wx.MessageBox('You must pick a skeleton file to change the bone name', 'Error')
			return


		if os.name == "posix":
			cmd = "meshmagick rename -bone=:" + before + ":" + after + ":" +  " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " rename -bone=:" + before + ":" + after + ":" +  " " + pth


		self.runCommand(cmd)


	def checkSkeleton(self, pth):
		skeleValue = self.skeletonRenamed.GetValue()

		if skeleValue == "":
			return

		if os.path.splitext(  os.path.split(pth)[1] )[1] != ".mesh": 
			wx.MessageBox('You must pick a mesh file to change the skeleton name', 'Error')
			return


		if os.name == "posix":
			cmd = "meshmagick rename -skeleton=" + skeleValue + " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " rename -skeleton=" + skeleValue + " " + pth

		self.runCommand(cmd)


	def checkMaterial(self, pth):
		after = self.materialAfter.GetValue()
		before = self.materialBefore.GetValue()

		if after == "" or before == "":
			return

		if os.path.splitext(  os.path.split(pth)[1] )[1] != ".mesh": 
			wx.MessageBox('You must pick a mesh file to change the material name', 'Error')
			return


		if os.name == "posix":
			cmd = "meshmagick rename -material=:" + before + ":" + after + ":" +  " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " rename -material=:" + before + ":" + after + ":" +  " " + pth



		self.runCommand(cmd)


	def checkAnimation(self, pth):
		after = self.animationAfter.GetValue()
		before = self.animationBefore.GetValue()

		if after == "" or before == "":
			return

		if os.path.splitext(  os.path.split(pth)[1] )[1] != ".skeleton": 
			wx.MessageBox('You must pick a skeleton file to change animation name', 'Error')
			return


		if os.name == "posix":
			cmd = "meshmagick rename -animation=:" + before + ":" + after + ":" +  " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " rename -animation=:" + before + ":" + after + ":" +  " " + pth

		self.runCommand(cmd)



	def checkSubMesh(self, pth):
		after = self.submeshAfter.GetValue()
		before = self.submeshBefore.GetValue()

		if after == "" or before == "":
			return

		if os.path.splitext(  os.path.split(pth)[1] )[1] != ".mesh": 
			wx.MessageBox('You must pick a mesh file to change submesh name', 'Error')
			return


		if os.name == "posix":
			cmd = "meshmagick rename -submesh=:" + before + ":" + after + ":" +  " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " rename -submesh=:" + before + ":" + after + ":" +  " " + pth

		self.runCommand(cmd)



	def OnOptimizeMesh(self, event):
		pathStr = self.meshFilePathBox.GetValue()
		if pathStr == "": 
			wx.MessageBox('You must pick a file to be optimized.', 'Error')
			return
		if os.path.splitext(  os.path.split(pathStr)[1] )[1] != ".mesh": 
			wx.MessageBox('You must pick a mesh file to change submesh name', 'Error')
			return

		if os.name == "posix":
			cmd = "meshmagick optimise " + pathStr + " " + pth
		else:
			pth = self.getFileName(pth)
			cmd = self.progL + " optimise " + pathStr + " " + pth


		self.runCommand(cmd)

	def OnclearChanges(self, event):
		self.rotate.SetValue("")
		self.translateX.SetValue("")
		self.translateY.SetValue("")
		self.translateZ.SetValue("")
		self.resizeX.SetValue("")
		self.resizeY.SetValue("")
		self.resizeZ.SetValue("")
		self.axesX.SetValue("")
		self.axesY.SetValue("")
		self.axesZ.SetValue("")
		self.scaleX.SetValue("")
		self.scaleY.SetValue("")
		self.scaleZ.SetValue("")
		self.submeshAfter.SetValue("")
		self.submeshBefore.SetValue("")
		self.skeletonRenamed.SetValue("")
		self.materialAfter.SetValue("")
		self.materialBefore.SetValue("")
		self.boneAfter.SetValue("")
		self.boneBefore.SetValue("")
		self.animationAfter.SetValue("")
		self.animationBefore.SetValue("")
		



	def onMediaDir(self, event): # wxGlade: MyFrame.<event_handler>
		dialog = wx.FileDialog ( None, "Please pick a mesh file")
		wildcard = "MESH files (*.mesh)|*.mesh|SKELETON files (*.skeleton)|*.skeleton"
		dialog.SetWildcard(wildcard)

		if dialog.ShowModal() == wx.ID_OK:
			slcted = dialog.GetPath()
			self.meshFilePathBox.SetValue(slcted )
		else:
			print 'Nothing was selected.'
			dialog.Destroy()

	def onAbout(self, event):
		wx.MessageBox('meshmagick gui was written by chieh', 'Info')

# end of class MyFrame



class MyApp(wx.App):
	def OnInit(self):

		if os.name == "posix":
			if not progExist( "meshmagick" ):
				wx.MessageBox('meshmagick was not found, did you install this program?', 'Error')
				sys.exit(1)

		wx.InitAllImageHandlers()
		frame_1 = MyFrame(None, -1, "")
		self.SetTopWindow(frame_1)
		frame_1.Show()
		return 1

# end of class MyApp

if __name__ == "__main__":
	app = MyApp(0)
	app.MainLoop()

User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by Beauty »

Bugreport:

The rename of a material doesn't work, but there is no error message.
Maybe this problem is fixed in the current version (if my one is outdated).
The tool doesn't tell its version number nor the year of creation.

Code: Select all

meshmagick rename -material=:cubematerial/TEXFACE:wall1: wall1.mesh
Loading mesh wall1.mesh...
Processing mesh...
Mesh saved as wall1.mesh.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

I just tested it and the rename tool works fine even when using special characters inside the name.
team-pantheon programmer
creators of Rastullahs Lockenpracht
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by Beauty »

I installed the current version and it works now.

Only in the wiki I saw the option -version.
This option is not displayed by
* starting meshmagick without parameter
* starting with -help
* starting with -list

The tool only tells the year 2007, so that user could think this verion is outdated.
Please add the version info to the "splash screen", which starts when you call meshmagick without parameter.


Also it would be better for overview if you add the tool overview (result of -list option) to the "splash screen".
For the list of tools it would be nicer to align the desctiptions.
The same for the help pages of tools.

Code: Select all

CURRENT:
info - print information about the mesh.
meshmerge - Merge multiple submeshes into a single mesh.
optimise - Optimise meshes and skeletons.
rename - Rename different elements of meshes and skeletons.
transform - Scale, rotate or otherwise transform a mesh.

BETTER:
info      - print information about the mesh.
meshmerge - Merge multiple submeshes into a single mesh.
optimise  - Optimise meshes and skeletons.
rename    - Rename different elements of meshes and skeletons.
transform - Scale, rotate or otherwise transform a mesh.

Somehow it would be nice to have a GUI for the command line tools.
Are they also available as dll file?
This would be more easy then parsing the output text.
Maybe there is still a tool like this? If so, it would be nice to add it (or a comment) to the OgreCommandLineTools setup.


The option -flip-vertex-winding you descripted in the wiki, but it's not listed by meshmagick -help=transform. The same for -axis and -flip-vertex-winding option. Please update it.


You removed the option -keep-file-version.
I hope this will not make trouble for mesh modifications of older applications that doesn't use the current Ogre version.


This are just some thoughts for improvement ideas.
Generally the tool is nice. Thanks for the wiki updates (:
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
User avatar
haffax
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4823
Joined: Fri Jun 18, 2004 1:40 pm
Location: Berlin, Germany
x 7
Contact:

Re: [New Tool] Ogre MeshMagick - First official release

Post by haffax »

Beauty, you're not testing the latest version.

It shows the version number (0.5.2 at the moment) in the -help option and all the supported transform commands are listed in the transform tool help. The -version option is listed in the global help too. Also meshmagick can be built as a dll/so already.

Some editors already have basic meshmagick support. Also only a few posts up juliusctw posted his meshmagick gui. ;)
team-pantheon programmer
creators of Rastullahs Lockenpracht
Post Reply