https://wiki.thedarkmod.com/api.php?action=feedcontributions&user=OrbWeaver&feedformat=atomThe DarkMod Wiki - User contributions [en]2024-03-28T15:16:31ZUser contributionsMediaWiki 1.39.5https://wiki.thedarkmod.com/index.php?title=Using_Blender_for_Doom_3_Modeling&diff=28737Using Blender for Doom 3 Modeling2021-10-20T19:46:17Z<p>OrbWeaver: Update ASE/LWO export scripts</p>
<hr />
<div>Posted originally by Magnesius:<br />
<br />
I am quite happy with Blender, and I would recommend it to everyone. Like with all more complex applications, you have to be ambitious until you get your first results.<br />
<br />
# Start with http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro. Although this wiki book has a badly chosen title, the beginning is helpful when you make your first steps with Blender. But beware - Blender is not really intuitive at the beginning. However, once you learned all the keyboard shortcuts, you'll quickly get into the workflow. I followed this tutorial up to the animation part of the Gingerbread man, and continued with...<br />
# http://blender3d.org/cms/Tutorials.243.0.html (esp. the "materials"-part is important). This is the official tutorial list, search there first when you want to learn something (e.g. animating a mesh). Take a look at several tutorials there, and extend your skills a bit.<br />
# Once you have created your first cube, your first vase and so on with Blender, you might want to export it to Doom.<br />
<br />
These tutorials are important, read them carefully:<br />
* http://www.katsbits.com/htm/tutorials/doom_3_prepping_models.htm<br />
* http://www.katsbits.com/htm/tutorials/doom_3_smooth_groups.htm<br />
<br />
==Export scripts==<br />
<br />
* [https://gitlab.com/orbweaver/darkblender ASE and LWO export scripts] for recent Blender versions (2.7x, 2.8x - 2.9x)<br />
<br />
===Obsolete scripts===<br />
<br />
These are very old scripts which will almost certainly not work in latest Blender versions. Using the latest ASE and LWO scripts above, manual material replacement is not necessary since the shader name can simply be entered as the Blender material name.<br />
<br />
* [[GoofosASEExporter | Snapshot of the ASE export script for Blender]] -- currently version 0.6.10<br />
* [[InstallASEScript | ASE installer script]] -- a small convenience script for installing an ASE file into a given location (i.e. under the models/ hierarchy) while automatically making texture name replacements from a list in a separate file.<br />
<br />
[[Category:Models]]</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=Visportals&diff=28736Visportals2021-10-20T19:41:26Z<p>OrbWeaver: Update troubleshooting section with minor formatting and mention DR's ability to load the pointfile without renaming.</p>
<hr />
<div>''written by Fidcal. Visportals, Func_statics and Worldspawn Brushes added by Sotha.''<br />
<br />
<br />
== Video Introduction to Visportals ==<br />
[https://youtu.be/RmKjmt7IJr8 Video Introduction to Visportals.]<br />
<br />
== What are visportals? ==<br />
Visportals are a very important part of mapping. They are a kind of brush that helps separate your map into individual areas (called visleafs), which is important for both rendering and sound propagation.<br />
<br />
Without visportals, the Doom engine would render the entire map even if the player can only see one small area. This would reduce performance and slow the game down. <br />
<br />
Visportals are also very important for sound propagation. Without them, sounds can travel through brushwork and reach areas of the map that they shouldn't (see ''Sound Propagation'' at the bottom of the page).<br />
<br />
<br />
Visportals determine how far the engine should render from the player's position. When a visportal is in the player's view, the engine tests to see if there is a second visportal viewable from the position of the first. If yes then it tests to see if a third one can be seen from the second one. If the third is not viewable from the first then the engine won't waste time rendering the map beyond it. If it is, then the engine tests for a fourth one and so on. So for example, with a long corridor with lots of 90 degree turns in it and visportals at every bend then the engine will only render the section you are in plus any parts of the next section you can see. But as you get near to it and can see further down it to the next bend's visportal then it will render the next one too.<br />
<br />
Anything in the visleaf that the player is currently in is rendered (if it is your field of view) regardless of whether it is behind worldspawn. But things in an adjacent visleaf will not be rendered if they are behind worldspawn. The engine still has to calculate what the player can see and what he can't, and this still uses resources, but far less than if it were in the same visleaf as the player. Any visleaf that is behind a closed portal is automatically beyond what the player can see, to the engine can skip testing it completely. This is why closed visportals are so valuable for performance.<br />
''[stgatilov --- on a minor note, the engine still has to check for shadows coming out from closed visleafs; so even a closed visleaf needs some processing]''<br />
<br />
The console command '''r_showPortals 1''' shows processed visportals as wireframe in-game for testing - even right through walls they are visible. A green wireframe is an 'open' visportal and renders everything beyond it up to the next visportal. A red wireframe is a 'closed' visportal with nothing rendered beyond it and no further visportals beyond that are even processed so are not visible at all. If a visportal does not show then it is either beyond range of processing or it is not working so check it is set up correctly.<br />
<br />
== What is rendering? ==<br />
Rendering might be regarded as the calculating of surface lighting, colour, texture, etc. Not all rendered surfaces are visible in-game but still have to be calculated. Others do not need rendering at all. Processing only takes place in the current portal area, that is, up to the nearest closed visportals. Surfaces are treated as divided into triangles for rendering purposes. The '''r_showTris''' console command shows those triangles as wireframes. In the console use '''r_showTris N''' where N is...<br />
<br />
*0 - Off - normal gameplay with no wireframes shown.<br />
*1 - Shows only triangles that are visible in-game (not hidden behind other surfaces.) These visible surfaces are only part of what is rendered.<br />
*2 - Shows as wireframes, all triangle surfaces that would be rendered in-game. This includes those hidden behind other surfaces BUT ONLY IF they face the player, even slightly. Surfaces facing away from the player are never rendered.<br />
*3 - Shows as wireframes, all triangle surfaces even if not rendered.<br />
<br />
You can see that for the purpose of testing performance use 2 above which shows all that would be rendered (whether visible to the player or not.) You can use 3 to see ''all'' triangles up to the next closed visportal but remember not all of it will be rendered so will not affect performance.<br />
<br />
You ''should'' also use '''r_useScissor 0''' to show geometry outside the visportal wireframe that Doom still sends to the video card. <br />
<br>Scissors happen on the GL driver side after geometry has already been uploaded. <br />
<br> They help with performance but the best performance happens when you prevent the geometry from being uploaded to your GPU in the first place.<br />
<br />
'''Remember''' to return to '''r_useScissor 1''' when you are done with "visportal testing" so that scissors can still <br />
<br>optimize away geometry that you were unable to optimize away with visportals.<br />
<br />
Here is a short video with a decent visual breakdown:<br />
<br />
[http://www.gdcvault.com/play/1014234/Excerpt-Quake-Postmortem-Optimizing-Level Video]<br />
<br />
Another video:<br />
<br />
[http://youtu.be/cy4FIE7BhCQ http://img.youtube.com/vi/cy4FIE7BhCQ/1.jpg]<br />
<br />
== How and where to create visportals ==<br />
<br />
Visportals are just brushes with one face covered in visportal texture. That is the operative face. Only be concerned with that one surface as the rest of the brush merely carries it. So to create one you just....<br />
<br />
* Make a thin brush like a wall. Ideally make it rectangular for best sound propagation (see ''Sound Propagation'' later.) It can extend beyond the gap so does not necessarily need to be the same shape - see ''Where to place your Visportal'' later.<br />
* Give it '''textures > editor > visportal''' to either of the main surfaces facing towards or away from you. It does not matter which except you need to be aware which it is because the visportal will be effective at that one face. (Use {{Ctrl}} + {{Shift}} + {{LMB}} on the face in the Dark Radiant camera view to select one face.)<br />
<br />
<br />
<br />
=== Thickness ===<br />
The exact thickness is not important to its effectiveness as it will only be effective from its one visportal-textured face anyway but it makes sense to keep it reasonably thin so you can see it clearly in the editor. Always think of visportals as one flat face and the rest of the brush can be ignored for functional purposes.<br />
<br />
<br />
<br />
=== Where to place your Visportal ===<br />
It will eventually be wedged tight like a plug into gaps, doorways, etc. between areas. The edges of the visportal-textured face of visportals should be completely sealed by or in opaque (i.e. solid) ''worldspawn'' brush that has no gaps. The brush '''must''' be a child of worldspawn and '''not''' an entity. This is true even if the visportal is within a door (see next section.) For the visportal to be effective it does not matter if the rest of the visportal brush sticks out into space so long as the edges of the visportal-textured face are surrounded by worldspawn brush and/or another visportal brush (see later) In practice you will probably put most fully within a corridor or doorway, a city street (yes, the sky above is solid worldspawn brush), etc. Irregular shaped gaps must still be plugged with a rectangular visportal which should then overlap into the solid worldspawn and leave no gaps. This will also make sound propagation work correctly (see ''Sound Propagation'' later.)<br />
<br />
Each area you thus created must be totally enclosed with worldspawn brushes with no gaps of either space else an entity. So a closed building with visportals at the doors but a tiny gap under the eaves of the roof won't work! Also avoid only blocking gaps with entities whether brushes or models. For example, a two-sided window entity that completely fills the opening will not seal the area for portalling purposes. Do not put in a visportal unless the window is either transparent or can be opened (like a door.) Instead put in a worldspawn brush with nodraw texture on it so it will not be visible or collidable just in front of the window. ''[not sure what this means, but note that nodraw textures DO NOT seal visleafs. As far as visportals are concerned, they don't exist]'' Or if you are using two windows back to back (say a lit one outside and an unlit one inside then a thin caulk brush between them.<br />
<br />
Generally you will place visportals on bends in the terrain around which the player cannot see from the current viewpoint. Avoid placing visportals in a line along a corridor or street for example. Since the player can see straight down through them all then they serve no purpose as they will all be open and visportals themselves create extra triangles and complexity (see downside section below.)<br />
<br />
With a large building surrounded by a walled open area then try visportals at each corner. If it is a very large building then also add visportals midway along each side. The player standing at one corner might be able to see up two sides, part of a far side, and even a small part of the fourth side so all sides would be rendered. By having extra visportals middway along each side then at least half of each side of the furthest corner need not be rendered (consider also preventing the player reaching such a viewpoint with an obstruction!)<br />
<br />
Subject to the above, try to find the smallest gaps where possible to place visportals. The smaller the visportal then the smaller the view aperture and so the less chance of lining up those apertures. Remember, visportals beyond the first one do not open unless they intersect all open visportals in that line of view. Even without a door, an open doorway is a better place for a visportal than a wider corridor or room beyond. Imagine trying to get a straight pole through as many of these visportal apertures as possible; the narrower and less aligned they are then the harder to get the pole very far.<br />
<br />
Don't get too hung up on perfect placement of every last visportal. Generally if you follow this guide and use your judgement you achieve a satisfactory game performance. Examine some large maps that you know work well to get some ideas where visportals are placed using the '''r_showPortals 1''' command.<br />
<br />
What if you put visportal texture on many faces of the visportal brush? Only one arbitrarily chosen face will be effective, and dmap will produce console warning about this situation. Basically, the engine does not support such brushes, so better use nodraw on all surfaces except the one face with visportal texture.<br />
<br />
===Multiple Joined Visportals===<br />
Because visportals are a single face, by their very nature they can only be placed on a single brush. But visportal brushes can be placed or merged together to fill more complex shaped gaps. For example, a wide rectangle might have one side clipped to slope over a roof while the rectangular part of its shape sits on top of another rectangle across the street below. But complexity affects performance so only do this if one rectangular visportal intruding into interior walls might do the same job. Remember, it does not matter if the visportal overlaps into solid so if there is an interior wall on the same plane (or indeed if the building is totally solid) then the visportal can penetrate it and still function. Do not use multiple visportals just to shape nicely around solid building trim and structure but intrude into it so long as the edge does not exit into space at any point it will be fine.<br />
<br />
== Visportals and doors ==<br />
A visportal in contact with a door that has an openable property automatically becomes "blocking" when the door is closed. Blocking visportals are always considered closed. In fact, they provide additional performance benefit over ordinary (i.e. nonblocking) closed visportals. For instance, objects behind ordinary closed visportals can still throw shadows which are visible to the player, so the engine has to process them. But blocking visportals block everything including light and shadows, so the renderer skips all visleafs behind them even when looking for shadow casters.<br />
<br />
Of course, when you open a closed door, its visportal becomes nonblocking/ordinary again, and switches between open and closed state as usual. So when you first go through a door and leave it open then its visportal will be open as you look immediately back at it. But as you then proceed further through other visportals, the door visportal will eventually close (prevent rendering through it) even if the door itself is left open.<br />
<br />
<br />
=== Doors standing in front of but not within a doorway ===<br />
Note that the visportal only needs to be in contact with the door to be affected by it. Normally you would place the visportal within the door but it actually only needs its visportal-textured surface to touch it. This means that if you have a door that is stood in front of an opening in a wall then you cannot put the visportal inside the door because it will not be surrounded by solid brush. But you can put a visportal just inside the opening and flush with the mouth of the opening and the door immediately in front of that and in contact with the visportal. You can test this by placing the door offset well to one side but part of it still in contact with the visportal. In game you can see the opening as the door is only partially covering it. When the door is closed the visportal closes and stops rendering through the doorway. You will see this as a black panel in the doorway. As the door opens the black panel disappears and you can see through the doorway again. You might think that from the other side the black panel would obscure the door when closed but it doesn't. With the door centred in its normal position, typically you might have an entity frame around the door. If you had a solid brush frame without gaps then the visportal could be inside the door if you wanted as it would then have solid all round its edges. In many cases of course, the door frame, whether entity or brush, would be within the solid brush doorway anyway.<br />
<br />
''Geep comments: This is pertinent for surface-mounted sliding doors. Unless they include transparency, such as ductwork grill doors (whether sliding or rotating), in which case the next section rules.''<br />
<br />
=== Windows, Doors with windows, bars, etc. ===<br />
Openable doors that let you see through them such as windows, doors with windows, barred gates, etc. should not be in contact with a visportal. When the door is closed it will automatically close any visportal whose effective surface is in touch with it. The visportal will not then permit any rendering through it and will show as a black panel in the window or between the bars of a gate. Instead, move the visportal away from the door. It will still function effectively as a normal visportal just as if the door were not there. This does not of course apply to simulated windows such as a glazed textured panel on a door texture where you cannot actually see through.<br />
<br />
All doors and windows that do not have an openable property can have a visportal in touch with them as it will not be affected by them and it will just function as a normal visportal.<br />
<br />
{{red|Rev. 2.00:}}<br />
<br />
Starting with Rev. 2.00, you can touch a see-through door or window with a visportal, and the portal will not close when the door/window is closed, as long as you use the following spawnarg '''on the door/window''' (not the info_portalsettings entity):<br />
<br />
* '''transparent''' "If set to 1 (0 by default), a see-through door can touch a visportal and the visportal won't close when the door is closed."<br />
<br />
This is useful when you want the opening or closing of the door to affect the sound loss across the portal, but not affect visibility through the door/window. Imagine a closed window that muffles a sound beyond it, and as the window opens, the sound gets louder.<br />
<br />
==Visportal switches: func_portals==<br />
<br />
The preceding section ''Visportals and doors'' makes clear that doors can function like switches to enable and close visportals in contact with them. ''func_portals'' can be used to enact further control over portals. You can place a ''func_portal'' entity so that it intersects with a visportal, and then trigger the func_portal to close or open the visportal. A second control method is had by using the ''portal_dist'' and ''distcheck_period'' properties on the ''func_portal''. They control the distance to the player and how frequently (in seconds) this distance should be checked, respectively, and make explicit triggering of the ''func_portal'' unnecessary. As with doors, even if enabled, the visportal will not be open even if it is in the player's view. Note, if you have a mover (door/window) in contact with the visportal, it will affect it as well, so typically a visportal controlled by func_portal is placed away from the mover, or use spawnarg "transparent" set to 1.<br />
<br />
Another clever trick exists with regard to ''func_portal''s: you can also build a func_static over the visportal, and target it with the ''func_portal'' as normal. Now, in addition to opening and closing the portal, this will also toggle the drawing of the ''func_static''. This means you could place an appropriate texture (perhaps a low resolution image of a room's interior, drawn over a window) and have it turn on or off based on the player's distance away. From a distance, the player wouldn't be able to tell that the the entire room wasn't being rendered. This can be an effective way to create the illusion of long view distances.<br />
<br />
Note that the func_static must touch the visportal in order to work.<br />
<br />
''One way to make this work is to create a patch, make it a func_static. Texture the patch, and then target the func_portal at that func_static. You must then add "hide" "1" to the func_static patch. There may be other ways to make this work, but I had a lot of trouble until I took these steps. -- Springheel''<br />
<br />
<br />
An interesting, recent discovery that needs some testing is that apparently: you can take one visportal and cut it in half, and then put different func_portals on each half in order to close them at different ranges. That has interesting implications for wide city streets. They require big visportals, but if you can split them up and independently control each part, there may be some benefits there. http://forums.thedarkmod.com/topic/9082-newbie-darkradiant-questions/?p=370473<br />
<br />
== Need I design, even compromise my map to make best use of visportals? ==<br />
You need to design your map from the start as a series of connected boxes (brushes) with the player's view, sound propogation, and visportals in mind. And yes, sometimes you might need to add a bend you don't want or obstruct a view in some way so you can put in another visportal if you are having or expecting slow down in a large and/or complex area. This is a matter of experience. If you have large complex areas and are experiencing frame lag but can see no obvious place to put a visportal then you might need to compromise your terrain and add some solid brush barriers to block certain views with visportals in the access round or through them.<br />
<br />
Note that it is not essential that visportals be only at the actual joins between brushes; they still work even if they are half way down a corridor so long as they are wedged in tight without gaps. But you will mostly place them at joins.<br />
<br />
For rendering considerations, the player's view is everything and every room and street in front of you even if there are walls, etc. in the way. Imagine what you would see with X-Ray vision. What is behind you or just out of range of the screen does not matter. But visportals restrict the view to what can be seen through the aperture of the visportal.<br />
<br />
<br />
It is not necessary to put visportals at every tiny bend in the map; in fact that should be avoided in smaller areas because visportals themselves create extra triangles and complexity (see downside section below.) Try to keep in mind what will be rendered through the visportals and judge what would ''not'' cause frame lag slowdown if the player could actually see that view. Three or four bends without visportals in a restricted air duct is not going to cause any problem if the duct is correctly visportalled at the ends for example.<br />
<br />
== Is there a downside? ==<br />
One downside to visportals is that is extra work for the mapper! Another is that they split the geometry and create extra triangles and batches. This will not normally cause a problem unless your map has a very large number of areas. The advantages in performance of using visportals far outweigh the downsides!<br />
<br />
<br />
<br />
==Sound Propagation==<br />
<br />
Correct propagation of sound (both to AI and to the player) relies on visportals.<br />
<br />
Think of vis-portals as sealing off separate "areas" in a map. A sound that occurs inside a sealed "area" will travel in a straight line to anyone inside the same area, even through walls. If you are not in the same area, however, the sound will travel in a straight line to the first visportal between you and the sound source, then to the next one, etc, until it reaches the area you are in.<br />
<br />
If the sound happens in an area that is not sealed, it essentially considers the whole map its "area", and will go straight to the player, through walls, floors, or other brushwork. This can result in confusion for players, as they will hear AI on the other side of a massive wall as if they were standing beside each other.<br />
<br />
In the following examples, "P" is the player and "S" is the sound source. The Visportals are red lines. The green lines show how the sound would travel.<br />
<br />
[[image:vis.jpg]]<br />
<br />
Without any visportals, the sound goes in a straight line to the player, even through solid walls. It will sound like the player is in the same room as the sound.<br />
<br />
One visportal is an improvement--it will sound further away, but the sound travels straight to the visportal and then straight to the player, so it will still sound like it is coming through the wall. <br />
<br />
Two visportals are best in this case, however. The sound will seem like it is coming from the opening to the room, which is where you would expect it to come from.<br />
<br />
<br />
Sound ''going to the AI'' travels the shortest distance through a visportal, passing at any point on the visportal surface. Mappers should always use rectangular visportals unless you have no choice with an awkwardly shaped gap or if the gap is out of range of AI, for example along a sloping roof where AI never go. '''It also still assumes that when you do have four sides, they are perpendicular.''' If you have some non-90 degree angles between the sides, some wierdness may ensue. For example, an AI that is alerted in the dark (or facing away) by hearing a sound from the player that passes through an irregular visportal might run to the center of the visportal rather than directly to where the player made the noise. Irregular visportals large enough to make this effect noticeable are likely to be uncommon.<br />
<br />
This will be okay for smaller irregular visportals, but the error (path difference from the minimal path) will get more noticeable the larger the visportal.<br />
<br />
Visportals function individually even if joined together into complex shapes. So a rectangle at ground level topped by an angled visportal above it tilting out over a roof will be treated separately, not as one aperture. <br />
<br />
The bottom line is, with any non-rectangular visportals keep the above in mind and if in doubt then consider early testing of AI's alert to player sound.<br />
<br />
Visportals do NOT need to be closed to correctly channel sound to the player. Sometimes it is worth adding visportals even if they will not help with rendering (because they will always be open) in order to make sound travel the way it should. <br />
<br />
To make less sound go through a visportal (through a small hole, for example):<br />
Soundprop can use info_locationSeparator or info_portalSettings to make sound incur a certain loss when going through the portal it's touching. The key/value pair is the following:<br />
<br />
sound_loss "Soundprop: Loss in dB incurred when a sound travels through this portal. Cumulative with door loss if a door is present." <br />
<br />
The loss must be a positive number, and negative numbers will automatically be converted to positive.<br />
<br />
{{red|Rev 2.00:}}<br />
<br />
Prior to Rev 2.00, '''sound_loss''' affected the sound propagation to AI '''only'''. Sound propagation to the player was not affected.<br />
<br />
Starting with Rev 2.00, '''sound_loss''' is also used in determining sound volume to the player. This aligns what AI hear with what the player hears. It also allows sound coming through the door to increase as the door opens, or decrease as the door closes, making for a more realistic experience.<br />
<br />
For details study: <br />
[[Sound Propagation: Part 1]] and<br />
[[Sound Propagation: Part 2]]<br />
<br />
<br />
<br />
== Engine changes ==<br />
<br />
Several bugs in dmap related to visportals were fixed in TDM 2.08. The technical description is provided [https://bugs.thedarkmod.com/view.php?id=5129#c12147 in issue #5129]. Here is the rough summary:<br />
<br />
# Normally, leaky visportals should be dropped by dmap, but a lot of them actually made it into the final maps. As a rule of thumb, a few dozens of functioning visportals in every FM released so far are leaky. Most of them cause no serious issues, although some of them cause thin black stripes where they leak. The new dmap drops all leaky visportals without exceptions. It means that if you compile an old FM with the new dmap, a lot of visportals will be lost.<br />
# A visportal had some weird effects outside of its bounds on its plane. This caused terrible trouble when several visportals lied on the same plane. Aside from that, it could produce ugly black panels on places without any visportals. All of this is now fixed, but there is a chance that some existing map somehow relied on the old erratic behavior.<br />
# Normally, if a brush has at least one non-opaque side (i.e. non-solid face), then it is considered non-opaque and does not seal visportals and areas. A bug caused some brushes with exactly one non-opaque side to be considered opaque. It is now fixed, but as the result some rare brushes on existing maps no longer seal areas if you dmap them with recent engine. This can lead to global leaks and dropped visportals.<br />
<br />
The changes seriously affect how visportalling works on existing maps. If you need to dmap an FM which was released before 2.08, then you have a choice:<br />
* Fix all the visportal warnings which dmap spits to console (see the next section). This will take some time and effort, and will probably need additional beta testing after that.<br />
* Enable old and buggy behavior of visportals, so that dmap works exactly as it did when FM was released. To achieve this, you have to set the following cvars (note that they do '''not''' persist between sessions):<br />
*# dmap_bspAllSidesOfVisportal 0<br />
*# dmap_fixVisportalOutOfBoundaryEffects 0<br />
*# dmap_fixBrushOpacityFirstSide 0<br />
<br />
== Troubleshooting ==<br />
<br />
Starting from TDM 2.08, dmap provides several useful diagnostics for visportalling problems. Every visportal with a problem is reported to the game console as a warning. You might want to [[Reporting Problem#condump|enable logFile]] to make it easier to read the whole dmap output. Aside from that, most warnings are accompanied by .lin files (generated near your .map), which can be viewed in DR.<br />
Below you can see the different types of warnings produced by dmap.<br />
<br />
;<tt>Portal %d at (%s) dropped</tt><br />
: The mentioned visportal does not separate the space into two isolated areas. This is usually called "internal leak". Such visportal is dropped because it appears to be fully contained in one visleaf, while every visportal must separate two different visleafs from each other.<br />
:Remember, the visportal face must be surrounded by opaque worldspawn brushes. Entities have no effect on visportals and visleafs, and brushes with any nodraw material are also ignored. If brief inspection does not help, load the .lin file in DR: it shows a closed path passing through the visportal which is not blocked by anything. In order to make visportal functional, you must block this path at least somewhere: the exact understanding of what went wrong and how to fix it is up to you.<br />
<br />
;<tt>Portal %d at (%s) dropped as leaky</tt><br />
:This is popular subcase of "dropped" warning, implemented in TDM 2.10.<br />
:A proper visportal must have all of its boundary edges fully in contact with opaque geometry or other visportals. You should see "as leaky" clarification when this rule is broken.<br />
:To fix the visportal, inspect its boundary, i.e. where it touches solid geometry.<br />
<br />
: '''TIPS:''' If you have any portals "dropped as leaky", save yourself some time by correcting them first. This will often stop other (good) visportals from being dropped. The .lin file discussed below will often show you exactly where the boundary problem is. Be aware that occasionally you may find a "dropped" portal that should have been tagged "dropped as leaky"; the detection algorithm is approximate.<br />
<br />
;<tt>Portals [%s] at (%s) overlap</tt><br />
:Two or more visportals geometrically overlap, which is going to be confuse the engine. In most cases the offending visportals are exactly equal, which is why it is very hard to notice without warning. Note that it is OK for visportals to touch each other.<br />
:In order to fix the problem, find both visportals in DR, and probably just delete one of them.<br />
<br />
;<tt>Portal %d at (%s) is useless</tt><br />
:No part of this visportal is within the playable space. Sometimes such visportal is fully inside opaque brushes or lie on the boundary of opaque brush. Sometimes the whole visportal hangs somewhere in outer space, outside of any playable locations.<br />
: Find this visportal in DR, verify that it is not necessary, and delete it.<br />
<br />
;<tt>brush %d has multiple area portal sides at %s</tt><br />
:More than one side/face of this brush is marked with visportal texture, which is not supported by engine. Remember that visportal brush must have exactly one face with visportal texture and all the other faces with nodraw texture.<br />
:First, find the brush. If any face has visportal material by mistake, change it to nodraw material. If both sides should work as visportals, then split the brush into two, each with one visportalled face.<br />
<br />
When investigating a particular warning, there are several ways to find and debug the visportal.<br />
# The brush number of the problematic portal is reported in the warning itself (like: <tt>Portal 5237</tt>). You can quickly find the brush in DR by using "Map -> Find Brush" feature, setting "Entity Number" to 0. Beware that brush numbering changes when you modify the map. If the numbers lead you to non-visportal brushes, then you most likely need to rerun dmap to get the new numbers.<br />
# The warning usually contains world 3D coordinates (like: <tt>at (347.25 -490 790)</tt>). These coordinates are much more stable that brush numbers and won't change between runs. You can use '''setviewpos''' in-game to teleport to specified coordinates, and hopefully DR will have such feature too in future.<br />
# For every dropped or overlapped visportal, a leakfile is dumped into a .lin file. The filename is reported right after the warning itself (like: <tt>saved mapname_portalL_coords.lin (%i points)</tt>. The capital letter after the word "portal" indicates the type of problem (e.g., with TDM 2.08, O = Overlap, L = dropped/Leaked; with TDM 2.10, O = Overlapped, D = Dropped, L = dropped as Leaky). The file is located in the same directory where .map file is located. In order to open it in DR, use the '''File → Pointfile''' menu option, which will show a dialog allowing you to choose the appropriate .lin file if there is more than one available. Use '''View → Camera → Next/Previous leak spot''' (or respectively '''Ctrl+Shift+K''' and '''Ctrl+Shift+L''') to teleport to leak location and travel along its points. Fly around: follow the path, see where it crosses something that you considered opaque while it is not. <br />
<br />
If you have fixed all warnings but some visportal doesn't work at all (i.e. does not show as either red or green while you are directly in its area and looking at it and you have '''r_showPortals''' enabled), then make sure the visportal is worldspawn, not some entity with a misapplied visportal texture.<br />
<br />
<br />
Expand the section below to read some outdated information. It is useless starting from 2.08.<br />
<div class="toccolours mw-collapsible mw-collapsed" style="width:100%; overflow:auto;><br />
<br />
There are two possible problems I can think of: the visportal doesn't work at all or it works yet there is too much being rendered giving poor performance and frame lag. The latter problem is a matter of experience and experimentation. Often much ingenuity is needed to place visportals to reduce the size of areas being rendered at any one time. There is probably a different solution for every situation. Ultimately you might be forced to change the terrain to help visportalling.<br />
<br />
If a visportal doesn't work at all (eg, does not show as either red or green while you are directly in its area and looking at it and you have '''r_showPortals''' enabled) then that means there is a "leak" in that ''area''. It does not necessarily mean the visportal itself is the problem.<br />
<br />
Try the following to track down the problem.<br />
<br />
'''Check the visportal itself:'''<br />
<br />
* Check which face of the visportal is facing which way. It sometimes happen one clones a visportal and rotates it the wrong way without realizing. So the wrong face is in the right place but the visportal face is not in contact with worldspawn.<br />
<br />
* Make sure the visportal hasn't been assigned to an entity but is worldspawn.<br />
<br />
* Search for gaps anywhere around the edges of the visportal face. Check for grid snapping not only of the visportal but the surrounding brushes. A tiny difference might not be visible in the Dark Radiant grid if you are not zoomed in sufficiently.<br />
<br />
* Check all surrounding items to see if any are entities. Remember, the visportal '''face''' must only be surrounded by worldspawn brushes. It seems to be mostly OK if the visportal ''cuts through'' an entity so long as it meets worldspawn at its edges.<br />
<br />
'''Check the area for internal leaks:'''<br />
<br />
There is a way to test an individual area for leaks. It sounds more complicated than it is.<br />
<br />
<br />
1. Filter off all entities. Make sure visportals are NOT filtered.<br />
<br />
2. Select your entire visible map (which will now be only brushwork).<br />
<br />
3. Copy and paste into a new blank map.<br />
<br />
4. Put the player start into the area where you think you have an internal leak. <br />
<br />
5. Take the visportals sealing that area and texture them with a solid texture.<br />
<br />
6. Make sure there is only void around the map (if you have your entire map encased in a skybox brush, open a hole in a distant corner of the map).<br />
<br />
7. Save the map as a new filename and run dmap. If you do not get a leak, then the area with the player start is properly sealed. Move the player start to a new area and repeat as needed.<br />
<br />
8. If you do get a leak, open the pointfile in DR. Because the player entity is the only entity in the map, the red line will always start there and will show you where the gap is.<br />
<br />
9. Go back to your original map and fix the gap. Repeat as needed.<br />
<br />
<br />
'''Having visportals that line up in parallel with each other can cause problems, usually appearing as pieces of wall that appear black or show skybox. This can usually be fixed by moving a visportal slightly or turning one on a slight angle so they're no longer parallel.'''<br />
<br />
<br />
'''If all else fails, try:'''<br />
<br />
* Clone the visportal and drag one edge to one side, that is, reduce its size to say half. <br />
* Select the original and drag its opposite edge the other way so you now have two visportals pressed together and which together fill the gap. Guess which side unless you suspect one side particularly (see later.)<br />
* Change one of the visportals to any normal visible simple texture, stone, wood, whatever so that now the original gap is smaller and filled by just one smaller visportal. (with hindsight, I think this would probably work anyway and might even be better just leaving it as visportal so you can see which one works but I'll continue with what I did!)<br />
* Check it in game.<br />
* If it still does not work then reduce it further, perhaps by half as much again. In particular, go past edges of surrounding brushes (eg, if working downwards with two brushes on the left then maybe it's the top brush that is the problem so reduce the top of the visportal below its bottom edge.) Obviously as you reduce the visportal you increase the solid temporary brush you made equally to keep up so there is never a gap.<br />
* If the visportal still does not work even when reduced to a slither then try the same for all four sides noting that the three sides against the slither of visportal are obviously first suspects.<br />
* If eventually the visportal starts to work then you know the error is in the other section where you have the solid temporary brush. Use a process of elimination to track down exactly where the error is.<br />
* You might even have to create a third temporary brush or even a fourth but you must eventually force the visportal to work even if you surround it with four new brushes! Once working, then reduce, remove, gradually until the error re-appears - that must be where the error lies. So by a process of elimination you should track down the source of the problem.<br />
* The result might make not seem to make sense but at least if you locate the error you can try things to solve the problem. I had a visportal up to the sky and on its left against the top of a triangle of bricks on which rested a roof. The visportal seemed to be perfectly surrounded by worldspawn but I reduced it slightly with just a slim temporary solid test brush only one unit high above it pressing against the top of this triangle. The visportal was now working yet if I removed that slim test brush and raised the visportal to the top again then it would not work. Made no sense to me unless mathematically the very top pixel of the triangle might be regarded as infinitely thin and therefore a gap. So, I left the thin test brush and gave it sky texture so it's completely invisible and everything works.<br />
<br />
(Note: Some mappers at Doom3world reported that cloned visportal brushes can cause problems in a map. This was reported in an earlier Doom 3 build.)<br />
<br />
</div><br />
<br />
== See also ==<br />
There are other good visportal tutorials to be found under:<br />
https://iddevnet.dhewm3.org/doom3/visportals.html and https://modwiki.dhewm3.org/Visportal<br />
<br />
== Visportals, Func_statics and Worldspawn Brushes ==<br />
Sometimes a visportal (VP) looks just fine in-game, but AI's behave erratically near them: cannot walk through the portal, walk into walls near the portal, can patrol through the portal but cannot chase the player through the portal. This may be due to worldspawn brushes penetrating the visportal.<br />
<br />
Image shows scenarios A-D of different VP placement.<br />
[[image:visportal_scenarios.png]]<br />
<br />
*Scenario A, when VP is snugly placed in the worldspawn corridor, it always works.<br />
*Scenario B, where a worldspawn brush penetrates the VP has often caused problems. It will fail with very high probability if the worldspawn brush is a monsterclip brush. Never do this scenario! ''[Springheel -- I'd like more confirmation on this...I'm certain I've done this on a regular basis in uneven city streets without causing any obvious problems]''<br />
*Scenario C is what could be used if the scene absolutely requires somethings going through the VP and scenario A or D cannot be used. Usually this must be done for big outdoors VP's as seen in Knightons Manor exterior behind the manor (it is the hedge, which goes through the VP there). Note how the worldspawn brush is cut just at the VP and it is only func_static which penetrates the VP. This has worked without issues. Note that no monsterclipping is required if the func_static segment of the VP penetrating brush is short enough. The blue worldspawn brushes could also be monsterclipped func_statics as well. If so, mapper must be certain monsterclip brushes do not penetrate the VP!<br />
*Scenario D where the VP is penetrated by func_static detail work, has not been reported to break visportals. Usually this kind of case is used in vaulted corridors etc. Works just fine. If the FS detail work is in reach of AI, special care to monsterclipping should be paid and it must be absolutely sure the monsterclip does not penetrate the VP as this will always cause trouble. In this scenario it is recommended that each side of the visportal have their own func_statics and one func_static brush should never penetrate more than one visportal.<br />
<br />
===Conclusion===<br />
Mappers should favour scenario A when possible. If detailwork requires, scenario D is okay, as well as scenario C. Never, ever use scenario B.<br />
<br />
If mapper has some issues with their AI pathfinding, they should check the portal isn't broken as in scenario B. It is really easy to forget wall trims etc which could pass through the VP. Also typical mistake is to automatically surround a model near the VP with monsterclip, which results monsterclip brush which penetrates the VP. It is recommended that the mapper tests every visportal (which an AI is likely to pass) in their map before the map is released. Firstly the mapper should check in DR and make sure visportals are correctly set. Ingame testing should be done both with non-alerted path_corner patrolling AI's and with an alerted AI chasing the player.<br />
<br />
== See also ==<br />
<br />
*[https://www.youtube.com/watch?v=cy4FIE7BhCQ Komag Tutorial for The Dark Mod: Visportals]<br />
*[http://forums.thedarkmod.com/topic/18948-have-we-been-selling-visportals-short/ Have we been selling visportals short?]<br />
*[http://forums.thedarkmod.com/topic/19707-diagonaly-placed-vis-portal/ Diagonally placed visportal?]<br />
*Visportals also help delineate Location Zones, with specific ambient lighting, ambient sounds, and enter/exit script events. See [[Location Settings]].<br />
<br />
[[Category:Skybox]]<br />
[[Category:Visportal]]<br />
{{tutorial-editing}}</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=Using_Blender_for_Doom_3_Modeling&diff=22568Using Blender for Doom 3 Modeling2019-01-20T15:15:43Z<p>OrbWeaver: Added current URL for ASE and LWO export scripts on GitLab.</p>
<hr />
<div>Posted originally by Magnesius:<br />
<br />
I am quite happy with Blender, and I would recommend it to everyone. Like with all more complex applications, you have to be ambitious until you get your first results.<br />
<br />
# Start with http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro. Although this wiki book has a badly chosen title, the beginning is helpful when you make your first steps with Blender. But beware - Blender is not really intuitive at the beginning. However, once you learned all the keyboard shortcuts, you'll quickly get into the workflow. I followed this tutorial up to the animation part of the Gingerbread man, and continued with...<br />
# http://blender3d.org/cms/Tutorials.243.0.html (esp. the "materials"-part is important). This is the official tutorial list, search there first when you want to learn something (e.g. animating a mesh). Take a look at several tutorials there, and extend your skills a bit.<br />
# Once you have created your first cube, your first vase and so on with Blender, you might want to export it to Doom.<br />
<br />
These tutorials are important, read them carefully:<br />
* http://www.katsbits.com/htm/tutorials/doom_3_prepping_models.htm<br />
* http://www.katsbits.com/htm/tutorials/doom_3_smooth_groups.htm<br />
<br />
Using Goofos ASE Exporter instead of the proposed USM Exporter, you can export multiple objects at once, you do not have to merge them. Get the script here or using the link below:<br />
<br />
http://www.doom3world.org/phpbb2/viewtopic.php?t=9275<br />
<br />
==Resources for Blender users==<br />
<br />
* [https://gitlab.com/orbweaver/darkblender ASE and LWO export scripts] for Blender 2.7x and Blender 2.80.<br />
<br />
===Obsolete scripts===<br />
<br />
These are very old scripts which may not even work in latest Blender versions. Using the latest ASE and LWO scripts above, manual material replacement is not necessary since the shader name can simply be entered as the Blender material name.<br />
<br />
* [[GoofosASEExporter | Snapshot of the ASE export script for Blender]] -- currently version 0.6.10<br />
* [[InstallASEScript | ASE installer script]] -- a small convenience script for installing an ASE file into a given location (i.e. under the models/ hierarchy) while automatically making texture name replacements from a list in a separate file.<br />
<br />
[[Category:Models]]</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=InstallASEScript&diff=6830InstallASEScript2008-08-23T09:13:03Z<p>OrbWeaver: Added content</p>
<hr />
<div>This is a small convenience script written in Python that can be used to quickly install an exported ASE while automatically making texture replacements, avoiding the time-consuming process of manually editing texture paths with a text editor after export.<br />
<br />
The texture replacements are stored in a file with the same name as the '''.ase''' but with '''.tex''' appended, such as '''myobject.ase.tex'''. This is a simple text file which can be stored in the directory that Blender exports to, from which the ASE is then installed into the model hierarchy using the Python script. The replacement file format is:<br />
<br />
<pre><br />
/exported/texture/name //base/textures/doom3/texture/name<br />
/exported/texture/name2 //base/textures/doom3/texture/name2<br />
</pre><br />
<br />
The Python script is then invoked like:<br />
<br />
$ install-ase.py blender/export/myobject.ase ~/.doom3/darkmod/models/mymodels/<br />
<br />
in order to install the exported ASE into the models hierarchy. In this case the replacement file would be '''blender/export/myobject.ase.tex'''<br />
<br />
'''NOTE:''' So far I have only tested this on Linux. Python does work on Windows but the pathname processing stuff might be different.<br />
<br />
=The script=<br />
<br />
<pre><br />
#!/usr/bin/python<br />
<br />
import sys<br />
import os.path<br />
<br />
if len(sys.argv) != 3:<br />
print "Usage: install-ase.py <filename.ase> <destination-dir>"<br />
print " A replacement file named <filename.ase.tex> must be present in "<br />
print " the source directory."<br />
sys.exit(1)<br />
<br />
# Check we can open the .ase file and its associated .ase.tex file containing<br />
# texture replacements<br />
try:<br />
aseFile = open(sys.argv[1], 'r')<br />
except IOError:<br />
print "Unable to open input file: %s" % sys.argv[1]<br />
sys.exit(2)<br />
<br />
# Build replacement mappings<br />
texReplacements = { }<br />
try:<br />
# Read the tex file<br />
texFile = open(sys.argv[1] + '.tex', 'r')<br />
for line in texFile:<br />
(first, second) = line.split()<br />
texReplacements[first] = second<br />
<br />
print "+ Making %d texture replacement(s):" % len(texReplacements.keys())<br />
for (k, v) in texReplacements.items():<br />
print "+ %s -> %s" % (k, v)<br />
except IOError:<br />
print "+ No replacement file"<br />
<br />
# Open the destination file<br />
srcFileName = os.path.split(aseFile.name)[1]<br />
destFileName = os.path.join(sys.argv[2], srcFileName)<br />
print "+ Installing as %s" % destFileName<br />
destFile = open(destFileName, 'w')<br />
<br />
# Copy the source file to the destination, making replacements<br />
for line in aseFile:<br />
for (old, new) in texReplacements.items():<br />
line = line.replace(old, new)<br />
destFile.write(line)<br />
destFile.close()<br />
<br />
</pre></div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=GoofosASEExporter&diff=6829GoofosASEExporter2008-08-23T09:06:27Z<p>OrbWeaver: Added copy of ASE export script</p>
<hr />
<div>=Blender ASE export script=<br />
<br />
This is a copy of the ASE export script for Blender written by Goofos on Doom3World.<br />
<br />
<pre><br />
#!BPY<br />
<br />
"""<br />
Name: 'ASCII Scene (.ase) v0.6.10'<br />
Blender: 244<br />
Group: 'Export'<br />
Tooltip: 'ASCII Scene Export (*.ase)'<br />
"""<br />
__author__ = "Goofos"<br />
__version__ = "0.6.10"<br />
__url__ = ["http://www.doom3world.org","http://www.doom3world.org/phpbb2/viewtopic.php?f=50&t=9275&st=0&sk=t&sd=a"]<br />
__bpydoc__ = """\<br />
-- ASCII Scene Export (.ase) export script v0.6.10 for Blender 2.44 --<br><br />
<br />
Can export:<br><br />
-Mesh Objects<br><br />
-Materials and Textures (no Procedural but Image)<br><br />
Note: Normalmaps will be exported as Bumpmaps (FixMe).<br />
Image path depends on how you have loaded it<br />
(absolute path's looks better :))<br />
Currently supported: Amb, Col, Csp, Hard, Alpha, Nor, Disp<br><br />
-Vertex Colors<br><br />
Note: If the mesh has materials you must enable "Vcol Paint"<br />
in Material tab. Without Materials, make sure "VertCol"<br />
in Mesh tab is enabled. Seems like the ASE Format doesn't<br />
support multiple Vertex Color layers.<br><br />
-Face UV<br><br />
Make sure "TexFace" in Mesh tab is enabled.<br />
Multi UV layers are now supported<br><br />
-Solid or Smooth Faces<br><br />
... smoothgroups currently only with a workaround. Solid<br />
faces will not have a smoothgroup, smooth faces will be by default in<br />
smoothgroup 1.<br><br />
<br />
-- Export Options Description --<br><br />
Apply Modifiers: Export the mesh with applied modifiers.<br />
Note: This uses the render settings of the modifiers.<br><br />
Materials: Export Materials if any.<br><br />
Face UV: Export TexFace UV if any. The current active UV Layer will be used<br />
as the first mapping channel.<br><br />
Vertex Colors: Export Vertex Colors if any (See note above). The<br />
current VC Layer will be used.<br><br />
Selection Only: Export only selected Objects or if nothing is selected<br />
all Objects.<br><br />
VertGr. as SmoothGr.: You can export SmoothGroups defined by<br />
VertexGroups. Simply create a VertGroup and name it "smooth." plus<br />
a group number, e.g. "smooth.2". Please note that you should not use<br />
more than 32 smoothgroups!<br />
Vertex Normals currently might not calculated right!!<br />
And there is a simple problem, if you add e.g. 3 faces of a cube<br />
to a smoothgroup, all 6 faces will be in the smoothgroup!! This is<br />
because the verts of the other 3 faces are in that group, too.<br />
You can see this if you select the vertexgroup.<br><br />
Center Objects: Move all objects to the World Grid Center.<br />
"""<br />
# goofos<br />
#<br />
# ***** BEGIN GPL LICENSE BLOCK *****<br />
#<br />
# This program is free software; you can redistribute it and/or<br />
# modify it under the terms of the GNU General Public License<br />
# as published by the Free Software Foundation; either version 2<br />
# of the License, or (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software Foundation,<br />
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.<br />
#<br />
# ***** END GPL LICENCE BLOCK *****<br />
<br />
import Blender, time, math, sys as osSys #os<br />
from Blender import sys, Window, Draw, Scene, Mesh, Material, Texture, Image, Mathutils<br />
<br />
#============================================<br />
# Write!<br />
#============================================<br />
<br />
def write(filename):<br />
start = time.clock()<br />
print_boxed('---------Start of Export------------')<br />
print 'Export Path: ' + filename<br />
<br />
global exp_list, Tab, idnt, imgTable, worldTable<br />
<br />
exp_list =[]<br />
Tab = "\t"<br />
idnt = 1<br />
matTable = {}<br />
worldTable = {'ambR': 0.0, 'ambG': 0.0, 'ambB': 0.0, 'horR': 0.0, 'horG': 0.0, 'horB': 0.0} #default<br />
total = {'Verts': 0, 'Tris': 0, 'Faces': 0}<br />
<br />
scn = Blender.Scene.GetCurrent()<br />
<br />
set_up(scn, exp_list, matTable, worldTable)<br />
if not exp_list:<br />
#if there is nothing to export, end here<br />
return<br />
<br />
file = open(filename, "w")<br />
write_header(file, filename, scn, worldTable)<br />
write_materials(file, exp_list, worldTable, matTable)<br />
write_mesh(file, scn, exp_list, matTable, total)<br />
file.close()<br />
<br />
Blender.Window.DrawProgressBar(0, "") # clear progressbar<br />
end = time.clock()<br />
seconds = " in %.2f %s" % (end-start, "seconds")<br />
totals = "Verts: %i Tris: %i Faces: %i" % (total['Verts'], total['Tris'], total['Faces'])<br />
print_boxed(totals)<br />
name = filename.split('/')[-1].split('\\')[-1]<br />
message = "Successfully exported " + name + seconds<br />
#meshtools.print_boxed(message)<br />
print_boxed(message)<br />
<br />
<br />
def print_boxed(text): #Copy/Paste from meshtools, only to remove the beep :)<br />
lines = text.splitlines()<br />
maxlinelen = max(map(len, lines))<br />
if osSys.platform[:3] == "win":<br />
print chr(218)+chr(196) + chr(196)*maxlinelen + chr(196)+chr(191)<br />
for line in lines:<br />
print chr(179) + ' ' + line.ljust(maxlinelen) + ' ' + chr(179)<br />
print chr(192)+chr(196) + chr(196)*maxlinelen + chr(196)+chr(217)<br />
else:<br />
print '+-' + '-'*maxlinelen + '-+'<br />
for line in lines: print '| ' + line.ljust(maxlinelen) + ' |'<br />
print '+-' + '-'*maxlinelen + '-+'<br />
#print '\a\r', # beep when done<br />
<br />
#============================================<br />
# Setup<br />
#============================================<br />
<br />
def set_up(scn, exp_list, matTable, worldTable):<br />
print "Setup"<br />
#Get selected Objects or if none selected all Objects from the current Scene<br />
if scn.objects.selected and guiTable['SELO'] == 1:<br />
objects = scn.objects.selected<br />
elif scn.objects:<br />
objects = scn.objects<br />
else:<br />
print "No Objects"<br />
return<br />
<br />
set_lists(exp_list, objects, matTable, worldTable)<br />
<br />
<br />
def set_lists(exp_list, objects, matTable, worldTable):<br />
global mat_cnt<br />
mat_cnt = 0<br />
mat_index = 0<br />
#exp_list = [container1 = [ [mesh], [material_ref] ],...]<br />
<br />
for current_obj in objects:<br />
container = []<br />
if current_obj.getType() == 'Mesh':<br />
container.append(current_obj)<br />
<br />
mat_type = 0 #1=Material, 2=UV Images<br />
mat_ref = []<br />
mesh = current_obj.data<br />
mats_me = mesh.materials<br />
mats_ob = current_obj.getMaterials(0)<br />
#Find used Materials by Meshes or Objects<br />
if guiTable['MTL'] == 1 and mats_me or mats_ob: #Materials<br />
if mats_me:<br />
me_mats = mats_me<br />
elif mats_ob:<br />
me_mats = mats_ob<br />
mat_ref = -1<br />
<br />
for i,m in matTable.iteritems():<br />
for mat in me_mats:<br />
if mat in m:<br />
for amat in me_mats:<br />
if amat not in m:<br />
m.append(amat)<br />
mat_ref = i<br />
break<br />
<br />
if mat_ref < 0:<br />
matTable[mat_index] = me_mats<br />
mat_ref = mat_index<br />
mat_cnt+=1<br />
mat_index+=1<br />
container.append(mat_ref)<br />
exp_list.append(container)<br />
<br />
#If there is a world shader get some values<br />
world = Blender.World.GetCurrent()<br />
if world != None:<br />
worldAmb = world.getAmb()<br />
worldHor = world.getHor()<br />
<br />
worldTable['ambR'] = worldAmb[0]<br />
worldTable['ambG'] = worldAmb[1]<br />
worldTable['ambB'] = worldAmb[2]<br />
<br />
worldTable['horR'] = worldHor[0]<br />
worldTable['horG'] = worldHor[1]<br />
worldTable['horB'] = worldHor[2]<br />
<br />
<br />
<br />
#============================================<br />
# Header/Scene<br />
#============================================<br />
<br />
def write_header(file, filename, scn, worldTable):<br />
print "Write Header"<br />
<br />
context = scn.getRenderingContext()<br />
<br />
file.write("*3DSMAX_ASCIIEXPORT%s200\n" % (Tab))<br />
file.write("*COMMENT \"Exported from Blender %s - %s\"\n" % (Blender.Get('version'), time.asctime(time.localtime())))<br />
file.write("*SCENE {\n")<br />
#file.write("%s*SCENE_FILENAME \"%s\"\n" % (Tab, os.path.basename(Blender.Get('filename'))))<br />
name = Blender.Get('filename').split('/')[-1].split('\\')[-1] #Blender 2.44<br />
file.write("%s*SCENE_FILENAME \"%s\"\n" % (Tab, name))<br />
<br />
file.write("%s*SCENE_FIRSTFRAME %d\n" % (Tab,context.startFrame()))<br />
file.write("%s*SCENE_LASTFRAME %d\n" % (Tab,context.endFrame()))<br />
file.write("%s*SCENE_FRAMESPEED %d\n" % (Tab,context.framesPerSec()))<br />
file.write("%s*SCENE_TICKSPERFRAME 160\n" % (Tab)) #Blender has no Ticks?<br />
<br />
file.write("%s*SCENE_BACKGROUND_STATIC %.4f %.4f %.4f\n" % (Tab, worldTable['horR'], worldTable['horG'], worldTable['horB']))<br />
file.write("%s*SCENE_AMBIENT_STATIC %.4f %.4f %.4f\n" % (Tab, worldTable['ambR'], worldTable['ambG'], worldTable['ambB']))<br />
file.write("}\n")<br />
<br />
<br />
#============================================<br />
# Materials<br />
#============================================<br />
<br />
def write_materials(file, exp_list, worldTable, matTable):<br />
print "Write Materials"<br />
<br />
file.write("*MATERIAL_LIST {\n")<br />
file.write("%s*MATERIAL_COUNT %s\n" % (Tab, mat_cnt))<br />
<br />
for i,m in matTable.iteritems():<br />
if len(m) == 1: # single mat<br />
mat_class = 'Standard'<br />
<br />
mats = m<br />
material = mats[0]<br />
mat_name = material.name<br />
<br />
file.write("%s*MATERIAL %d {\n" % ((Tab), i))<br />
<br />
idnt = 2<br />
mat_para(file, idnt, material, mat_name, mat_class, worldTable)<br />
mat_dummy(file, idnt)<br />
mat_map(file, idnt, mat_name)<br />
<br />
file.write("%s}\n" % (Tab))<br />
<br />
elif len(m) > 1: # multiple mat<br />
mat_class = 'Multi/Sub-Object'<br />
<br />
mats = m<br />
material = mats[0]<br />
mat_name = 'Multi # ' + material.name<br />
submat_no = len(mats)<br />
<br />
idnt = 2<br />
file.write("%s*MATERIAL %d {\n" % ((Tab), i))<br />
<br />
mat_para(file, idnt, material, mat_name, mat_class, worldTable)<br />
<br />
file.write("%s*NUMSUBMTLS %d\n" % ((Tab*idnt), submat_no))<br />
<br />
for submat_cnt,current_mat in enumerate(mats):<br />
material = current_mat<br />
mat_class = 'Standard'<br />
mat_name = material.name<br />
<br />
idnt = 2<br />
file.write("%s*SUBMATERIAL %d {\n" % ((Tab*idnt), submat_cnt))<br />
submat_cnt += 1<br />
<br />
idnt = 3<br />
mat_para(file, idnt, material, mat_name, mat_class, worldTable)<br />
mat_dummy(file, idnt)<br />
mat_map(file, idnt, mat_name)<br />
<br />
idnt = 2<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
file.write("%s}\n" % (Tab))<br />
<br />
<br />
file.write("}\n")<br />
<br />
<br />
def mat_para(file, idnt, material, mat_name, mat_class, worldTable):<br />
<br />
mat_amb = material.getAmb()<br />
mat_dif = material.getRGBCol()<br />
mat_specCol = material.getSpecCol()<br />
mat_spec = material.getSpec()<br />
mat_hard = material.getHardness()<br />
mat_alpha = 1.0000-material.getAlpha()<br />
<br />
file.write("%s*MATERIAL_NAME \"%s\"\n" % ((Tab*idnt), mat_name))<br />
file.write("%s*MATERIAL_CLASS \"%s\"\n" % ((Tab*idnt), mat_class))<br />
file.write("%s*MATERIAL_AMBIENT %.4f %.4f %.4f\n" % ((Tab*idnt), (worldTable['ambR']*mat_amb), (worldTable['ambG']*mat_amb), (worldTable['ambB']*mat_amb))) #-Usefull?<br />
file.write("%s*MATERIAL_DIFFUSE %.4f %.4f %.4f\n" % ((Tab*idnt), mat_dif[0], mat_dif[1], mat_dif[2]))<br />
file.write("%s*MATERIAL_SPECULAR %.4f %.4f %.4f\n" % ((Tab*idnt), mat_specCol[0], mat_specCol[1], mat_specCol[2]))<br />
file.write("%s*MATERIAL_SHINE %.4f\n" % ((Tab*idnt), mat_spec))<br />
file.write("%s*MATERIAL_SHINESTRENGTH %.4f\n" % ((Tab*idnt), (mat_hard/511.))) #-511 or 512?<br />
file.write("%s*MATERIAL_TRANSPARENCY %.4f\n" % ((Tab*idnt), mat_alpha))<br />
file.write("%s*MATERIAL_WIRESIZE 1.0000\n" % (Tab*idnt))<br />
<br />
<br />
def mat_dummy(file, idnt):<br />
<br />
file.write("%s*MATERIAL_SHADING Blinn\n" % (Tab*idnt))<br />
file.write("%s*MATERIAL_XP_FALLOFF 0.0000\n" % (Tab*idnt))<br />
file.write("%s*MATERIAL_SELFILLUM 0.0000\n" % (Tab*idnt))<br />
file.write("%s*MATERIAL_FALLOFF In\n" % (Tab*idnt))<br />
file.write("%s*MATERIAL_XP_TYPE Filter\n" % (Tab*idnt))<br />
<br />
<br />
def mat_map(file, idnt, mat_name):<br />
<br />
mapTable = {0:'*MAP_AMBIENT',1:'*MAP_DIFFUSE',2:'*MAP_SPECULAR',3:'*MAP_SHINE',4:'*MAP_SHINESTRENGTH',5:'*MAP_SELFILLUM',6:'*MAP_OPACITY',7:'*MAP_FILTERCOLOR',8:'*MAP_BUMP',9:'*MAP_REFLECT',10:'*MAP_REFRACT',11:'*MAP_REFRACT'}<br />
tex_list = [[],[],[],[],[],[],[],[],[],[],[],[]]<br />
<br />
mat = Material.Get(mat_name)<br />
MTexes = mat.getTextures()<br />
<br />
for current_MTex in MTexes:<br />
if current_MTex is not None:<br />
# MAP_SUBNO 0 = *MAP_AMBIENT<br />
if current_MTex.mapto & Texture.MapTo.AMB:<br />
map_getTex(current_MTex, 0, (current_MTex.dvar*current_MTex.varfac), tex_list)<br />
# MAP_SUBNO 1 = *MAP_DIFFUSE = COL = 1<br />
elif current_MTex.mapto & Texture.MapTo.COL:<br />
map_getTex(current_MTex, 1, current_MTex.colfac, tex_list)<br />
# MAP_SUBNO 2 = *MAP_SPECULAR (Color)= CSP or SPEC? = 4<br />
elif current_MTex.mapto & Texture.MapTo.CSP:<br />
map_getTex(current_MTex, 2, current_MTex.colfac, tex_list)<br />
# MAP_SUBNO 3 = *MAP_SHINE (Spec Level) = SPEC or CSP? = 32<br />
elif current_MTex.mapto & Texture.MapTo.SPEC:<br />
map_getTex(current_MTex, 3, (current_MTex.dvar*current_MTex.varfac), tex_list)<br />
# MAP_SUBNO 4 = *MAP_SHINESTRENGTH (Gloss) = HARD = 256<br />
elif current_MTex.mapto & Texture.MapTo.HARD:<br />
map_getTex(current_MTex, 4, (current_MTex.dvar*current_MTex.varfac), tex_list)<br />
# MAP_SUBNO 5 = *MAP_SELFILLUM<br />
# MAP_SUBNO 6 = *MAP_OPACITY = ALPHA = 128<br />
elif current_MTex.mapto & Texture.MapTo.ALPHA:<br />
map_getTex(current_MTex, 6, (current_MTex.dvar*current_MTex.varfac), tex_list)<br />
# MAP_SUBNO 7 = *MAP_FILTERCOLOR<br />
# MAP_SUBNO 8 = *MAP_BUMP = NOR = 2<br />
elif current_MTex.mapto & Texture.MapTo.NOR:<br />
map_getTex(current_MTex, 8, (current_MTex.norfac/25), tex_list)<br />
# MAP_SUBNO 9 = *MAP_REFLECT<br />
elif current_MTex.mapto & Texture.MapTo.REF:<br />
map_getTex(current_MTex, 9, (current_MTex.norfac/25), tex_list)<br />
# MAP_SUBNO 10 = *MAP_REFRACT (refraction)<br />
# MAP_SUBNO 11 = *MAP_REFRACT (displacement)<br />
elif current_MTex.mapto & Texture.MapTo.DISP:<br />
map_getTex(current_MTex, 11, (current_MTex.norfac/25), tex_list)<br />
<br />
# Write maps<br />
for current_LI in tex_list:<br />
subNo = tex_list.index(current_LI)<br />
for current_MTex in current_LI:<br />
tex = current_MTex[0].tex<br />
if tex.type == Texture.Types.IMAGE:<br />
map_image(file, idnt, current_MTex, subNo, tex, mapTable[subNo])<br />
<br />
<br />
def map_getTex(MTex, map_subNo, map_amount, texes):<br />
# container = [[[MTex], [map_amount]], ...]<br />
container = []<br />
container.append(MTex)<br />
container.append(map_amount)<br />
texes[map_subNo].append(container)<br />
<br />
<br />
def map_image(file, idnt, MTexCon, subNo, tex, mapType):<br />
<br />
img = tex.getImage()<br />
#path = sys.expandpath(img.getFilename()).replace('/', '\\')<br />
path = img.filename #or img.getFilename()<br />
tex_class = 'Bitmap'<br />
tex_mapType = 'Screen'<br />
tex_filter = 'Pyramidal'<br />
<br />
file.write("%s%s {\n" % ((Tab*idnt), mapType))<br />
<br />
idnt += 1<br />
file.write("%s*MAP_NAME \"%s\"\n" % ((Tab*idnt), tex.getName()))<br />
file.write("%s*MAP_CLASS \"%s\"\n" % ((Tab*idnt), tex_class))<br />
file.write("%s*MAP_SUBNO %s\n" % ((Tab*idnt), subNo))<br />
file.write("%s*MAP_AMOUNT %.4f\n" % ((Tab*idnt), MTexCon[1]))<br />
file.write("%s*BITMAP \"%s\"\n" % ((Tab*idnt), path))<br />
file.write("%s*MAP_TYPE %s\n" % ((Tab*idnt), tex_mapType))<br />
<br />
# hope this part is right!<br />
u_tiling = tex.repeat[0]*tex.crop[2]<br />
v_tiling = tex.repeat[1]*tex.crop[3]<br />
file.write("%s*UVW_U_OFFSET %.4f\n" % ((Tab*idnt), tex.crop[0]))<br />
file.write("%s*UVW_V_OFFSET %.4f\n" % ((Tab*idnt), tex.crop[1]))<br />
file.write("%s*UVW_U_TILING %.4f\n" % ((Tab*idnt), u_tiling))<br />
file.write("%s*UVW_V_TILING %.4f\n" % ((Tab*idnt), v_tiling))<br />
<br />
map_uvw(file, idnt) #hardcoded<br />
<br />
file.write("%s*BITMAP_FILTER %s\n" % ((Tab*idnt), tex_filter))<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mat_uv(file, idnt, uv_image, uv_name, mat_class, worldTable):<br />
fake_val0 = '0.0000'<br />
fake_val1 = '0.1000'<br />
fake_val2 = '0.5882'<br />
fake_val3 = '0.9000'<br />
fake_val4 = '1.0000'<br />
<br />
file.write("%s*MATERIAL_NAME \"%s\"\n" % ((Tab*idnt), uv_name))<br />
file.write("%s*MATERIAL_CLASS \"%s\"\n" % ((Tab*idnt), mat_class))<br />
file.write("%s*MATERIAL_AMBIENT %.4f %.4f %.4f\n" % ((Tab*idnt), worldTable['ambR'], worldTable['ambG'], worldTable['ambB'])) #------------Usefull?<br />
file.write("%s*MATERIAL_DIFFUSE %s %s %s\n" % ((Tab*idnt), fake_val2, fake_val2, fake_val2))<br />
file.write("%s*MATERIAL_SPECULAR %s %s %s\n" % ((Tab*idnt), fake_val3, fake_val3, fake_val3))<br />
file.write("%s*MATERIAL_SHINE %s\n" % ((Tab*idnt), fake_val1))<br />
file.write("%s*MATERIAL_SHINESTRENGTH %s\n" % ((Tab*idnt), fake_val0))<br />
file.write("%s*MATERIAL_TRANSPARENCY %s\n" % ((Tab*idnt), fake_val0))<br />
file.write("%s*MATERIAL_WIRESIZE %s\n" % ((Tab*idnt), fake_val4))<br />
<br />
<br />
def map_uv(file, idnt, uv_image, uv_name):<br />
map_type = '*MAP_DIFFUSE'<br />
map_subNo = '1'<br />
tex_class = 'Bitmap'<br />
tex_mapType = 'Screen'<br />
tex_filter = 'Pyramidal'<br />
<br />
fake_val0 = '0.0000'<br />
fake_val1 = '0.1000'<br />
fake_val2 = '0.5882'<br />
fake_val3 = '0.9000'<br />
fake_val4 = '1.0000'<br />
<br />
#replace "/" with "\" in image path<br />
uv_filename = uv_image.getFilename().replace('/', '\\')<br />
<br />
file.write("%s%s {\n" % ((Tab*idnt), map_type))<br />
<br />
idnt += 1<br />
file.write("%s*MAP_NAME \"%s\"\n" % ((Tab*idnt), uv_name))<br />
file.write("%s*MAP_CLASS \"%s\"\n" % ((Tab*idnt), tex_class))<br />
file.write("%s*MAP_SUBNO %s\n" % ((Tab*idnt), map_subNo))<br />
file.write("%s*MAP_AMOUNT %s\n" % ((Tab*idnt), fake_val4))<br />
file.write("%s*BITMAP \"%s\"\n" % ((Tab*idnt), uv_filename))<br />
file.write("%s*MAP_TYPE %s\n" % ((Tab*idnt), tex_mapType))<br />
file.write("%s*UVW_U_OFFSET %s\n" % ((Tab*idnt), fake_val0))<br />
file.write("%s*UVW_V_OFFSET %s\n" % ((Tab*idnt), fake_val0))<br />
file.write("%s*UVW_U_TILING %s\n" % ((Tab*idnt), fake_val4))<br />
file.write("%s*UVW_V_TILING %s\n" % ((Tab*idnt), fake_val4))<br />
<br />
map_uvw(file, idnt) #hardcoded<br />
<br />
file.write("%s*BITMAP_FILTER %s\n" % ((Tab*idnt), tex_filter))<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def map_uvw(file, idnt):<br />
<br />
fake_val0 = '0.0000'<br />
fake_val1 = '1.0000'<br />
<br />
file.write("%s*UVW_ANGLE %s\n" % ((Tab*idnt), fake_val0))<br />
file.write("%s*UVW_BLUR %s\n" % ((Tab*idnt), fake_val1))<br />
file.write("%s*UVW_BLUR_OFFSET %s\n" % ((Tab*idnt), fake_val0))<br />
file.write("%s*UVW_NOUSE_AMT %s\n" % ((Tab*idnt), fake_val1))<br />
file.write("%s*UVW_NOISE_SIZE %s\n" % ((Tab*idnt), fake_val1))<br />
file.write("%s*UVW_NOISE_LEVEL 1\n" % (Tab*idnt))<br />
file.write("%s*UVW_NOISE_PHASE %s\n" % ((Tab*idnt), fake_val0))<br />
<br />
<br />
#============================================<br />
# Mesh<br />
#============================================<br />
<br />
<br />
def write_mesh(file, scn, exp_list, matTable, total):<br />
print "Write Geometric"<br />
<br />
for current_container in exp_list:<br />
<br />
TransTable = {'SizeX': 1, 'SizeY': 1, 'SizeZ': 1}<br />
nameMe = {'objName': 'obj', 'meName': 'me'}<br />
sGroups = {}<br />
hasTable = {'hasMat': 0, 'hasSG': 0, 'hasUV': 0, 'hasVC': 0, 'matRef': 0}<br />
count = {'face': 0, 'vert': 0, 'UVs': 0, 'cVert': 0}<br />
<br />
obj = current_container[0]<br />
#mat_ref = current_container[1]<br />
data = obj.getData(0,1)<br />
nameMe['objName'] = obj.name<br />
nameMe['meName'] = data.name<br />
<br />
mats_me = [mat for mat in data.materials if mat] #fix for 2.44, get rid of NoneType Objects in me.materials<br />
mats_ob = obj.getMaterials(0)<br />
materials = False<br />
<br />
if mats_me:<br />
materials = mats_me<br />
elif mats_ob:<br />
materials = mats_ob<br />
<br />
if guiTable['MTL'] and materials:<br />
hasTable['hasMat'] = 1<br />
hasTable['matRef'] = current_container[1]<br />
<br />
if obj.getParent():<br />
nameMe['parent'] = obj.getParent().name<br />
<br />
me = Mesh.New() # Create a new mesh<br />
<br />
if guiTable['MOD']: # Use modified mesh<br />
me.getFromObject(obj.name, 0) # Get the object's mesh data, cage 0 = apply mod<br />
else:<br />
me.getFromObject(obj.name, 1)<br />
<br />
me.transform(obj.matrix) # ASE stores transformed mesh data<br />
if guiTable['RECENTER']: # Recentre Objects to 0,0,0 feature<br />
rec_matrix = Mathutils.TranslationMatrix(obj.matrix.translationPart().negate())<br />
me.transform(rec_matrix)<br />
<br />
tempObj = Blender.Object.New('Mesh', 'ASE_export_temp_obj')<br />
tempObj.setMatrix(obj.matrix)<br />
tempObj.link(me)<br />
<br />
if guiTable['VG2SG']:<br />
VGNames = data.getVertGroupNames()<br />
for vg in VGNames:<br />
me.addVertGroup(vg)<br />
gverts = data.getVertsFromGroup(vg, 1)<br />
gverts_copy = []<br />
for gv in gverts:<br />
gverts_copy.append(gv[0])<br />
me.assignVertsToGroup(vg, gverts_copy, 1, 1)<br />
<br />
obj = tempObj<br />
faces = me.faces<br />
verts = me.verts<br />
<br />
count['vert'] = len(verts)<br />
total['Verts'] += count['vert']<br />
<br />
if count['vert'] == 0:<br />
print 'Error: ' + nameMe['meName'] + 'has 0 Verts'<br />
continue<br />
<br />
vGroups = me.getVertGroupNames()<br />
if guiTable['VG2SG'] and len(vGroups) > 0:<br />
for current_VG in vGroups:<br />
if current_VG.lower().count("smooth."):<br />
hasTable['hasSG'] = 1<br />
smooth_num = int(current_VG.lower().replace("smooth.", ""))<br />
gverts = me.getVertsFromGroup(current_VG)<br />
for vi in gverts:<br />
if not sGroups.has_key(vi):<br />
sGroups[vi] = [smooth_num]<br />
else:<br />
sGroups[vi].append(smooth_num)<br />
<br />
if guiTable['UV']:<br />
if me.faceUV == True or me.faceUV == 1:<br />
hasTable['hasUV'] = 1<br />
<br />
if guiTable['VC']:<br />
if me.vertexColors:<br />
hasTable['hasVC'] = 1<br />
elif hasTable['hasMat']: # Blender material<br />
for current_mat in materials:<br />
if current_mat.getMode() & Material.Modes['VCOL_PAINT']:<br />
hasTable['hasVC'] = 1<br />
break<br />
<br />
for current_face in faces:<br />
if len(current_face.verts) is 3:<br />
count['face'] += 1<br />
total['Tris'] += 1<br />
total['Faces'] += 1<br />
elif len(current_face.verts) is 4:<br />
count['face'] += 2<br />
total['Tris'] += 2<br />
total['Faces'] += 1<br />
<br />
#Open Geomobject<br />
file.write("*GEOMOBJECT {\n")<br />
file.write("%s*NODE_NAME \"%s\"\n" % (Tab, nameMe['objName']))<br />
<br />
if nameMe.has_key('parent'):<br />
file.write("%s*NODE_PARENT \"%s\"\n" % (Tab, nameMe['parent']))<br />
<br />
idnt = 1<br />
mesh_matrix(file, idnt, obj, nameMe, TransTable)<br />
<br />
#Open Mesh<br />
file.write("%s*MESH {\n" % (Tab))<br />
<br />
idnt = 2<br />
file.write("%s*TIMEVALUE 0\n" % (Tab*idnt))<br />
file.write("%s*MESH_NUMVERTEX %i\n" % ((Tab*idnt), count['vert']))<br />
file.write("%s*MESH_NUMFACES %i\n" % ((Tab*idnt), count['face']))<br />
<br />
idnt = 2<br />
mesh_vertexList(file, idnt, verts, count)<br />
idnt = 2<br />
mesh_faceList(file, idnt, me, materials, sGroups, faces, matTable, hasTable, count)<br />
<br />
<br />
if hasTable['hasUV'] == 1:<br />
UVTable = {}<br />
<br />
active_map_channel = me.activeUVLayer<br />
map_channels = me.getUVLayerNames()<br />
<br />
idnt = 2<br />
mesh_tVertList(file, idnt, faces, UVTable, count)<br />
#idnt = 2<br />
mesh_tFaceList(file, idnt, faces, UVTable, count)<br />
UVTable = {}<br />
<br />
if len(map_channels) > 1:<br />
chan_index = 2<br />
for map_chan in map_channels:<br />
if map_chan != active_map_channel:<br />
me.activeUVLayer = map_chan<br />
<br />
idnt = 2<br />
file.write("%s*MESH_MAPPINGCHANNEL %i {\n" % ((Tab*idnt), chan_index))<br />
idnt = 3<br />
mesh_tVertList(file, idnt, faces, UVTable, count)<br />
mesh_tFaceList(file, idnt, faces, UVTable, count)<br />
UVTable = {}<br />
chan_index += 1<br />
idnt = 2<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
me.activeUVLayer = active_map_channel<br />
<br />
else:<br />
# dirty fix<br />
file.write("%s*MESH_NUMTVERTEX %i\n" % ((Tab*idnt), count['UVs']))<br />
<br />
if hasTable['hasVC'] == 1:<br />
cVertTable = {}<br />
<br />
idnt = 2<br />
mesh_cVertList(file, idnt, faces, cVertTable, count)<br />
#idnt = 2<br />
mesh_cFaceList(file, idnt, faces, cVertTable, count)<br />
else:<br />
# dirty fix<br />
file.write("%s*MESH_NUMCVERTEX %i\n" % ((Tab*idnt), count['cVert']))<br />
<br />
<br />
idnt = 2<br />
mesh_normals(file, idnt, faces, verts, count)<br />
<br />
# Close *MESH<br />
idnt = 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
idnt = 1<br />
mesh_footer(file, idnt, hasTable)<br />
<br />
# Close *GEOMOBJECT<br />
file.write("}\n")<br />
<br />
#free some memory<br />
me.materials = [None]<br />
me.faces.delete(1,[(f.index) for f in me.faces])<br />
me.verts.delete(me.verts)<br />
obj.fakeUser = False<br />
me.fakeUser = False<br />
scn.objects.unlink(obj)<br />
<br />
def mesh_matrix(file, idnt, obj, nameMe, TransTable):<br />
<br />
#i should check why i have to get and invert the matrix<br />
#exactly in that sequence.<br />
<br />
row = obj.getMatrix('localspace').invert()<br />
#row = obj.getInverseMatrix()<br />
<br />
if guiTable['RECENTER']:<br />
location = 0.0,0.0,0.0<br />
row[3][0] = row[3][1] = row[3][2] = 0.0<br />
else:<br />
location = obj.getLocation()<br />
<br />
quat = row.invert().toQuat()<br />
#quat = obj.getMatrix('localspace').toQuat()<br />
rota = quat.axis<br />
#angle = quat.angle * (math.pi/180) #Blender: degrees -> ASE: radians<br />
angle = math.radians(quat.angle)<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing Transform Node")<br />
<br />
file.write("%s*NODE_TM {\n" % (Tab*idnt))<br />
<br />
idnt += 1<br />
file.write("%s*NODE_NAME \"%s\"\n" % ((Tab*idnt), nameMe['meName']))<br />
# Inherit from what?..<br />
file.write("%s*INHERIT_POS 0 0 0\n" % (Tab*idnt))<br />
file.write("%s*INHERIT_ROT 0 0 0\n" % (Tab*idnt))<br />
file.write("%s*INHERIT_SCL 0 0 0\n" % (Tab*idnt))<br />
<br />
file.write("%s*TM_ROW0 %.4f %.4f %.4f\n" % ((Tab*idnt), row[0][0], row[0][1], row[0][2]))<br />
file.write("%s*TM_ROW1 %.4f %.4f %.4f\n" % ((Tab*idnt), row[1][0], row[1][1], row[1][2]))<br />
file.write("%s*TM_ROW2 %.4f %.4f %.4f\n" % ((Tab*idnt), row[2][0], row[2][1], row[2][2]))<br />
file.write("%s*TM_ROW3 %.4f %.4f %.4f\n" % ((Tab*idnt), row[3][0], row[3][1], row[3][2]))<br />
<br />
file.write("%s*TM_POS %.4f %.4f %.4f\n" % ((Tab*idnt), location[0], location[1], location[2]))<br />
<br />
file.write("%s*TM_ROTAXIS %.4f %.4f %.4f\n" % ((Tab*idnt), rota.x, rota.y, rota.z))<br />
file.write("%s*TM_ROTANGLE %.4f\n" % ((Tab*idnt), angle))<br />
<br />
file.write("%s*TM_SCALE %.4f %.4f %.4f\n" % ((Tab*idnt), TransTable['SizeX'], TransTable['SizeY'], TransTable['SizeZ']))<br />
#file.write("%s*TM_SCALEAXIS 0.0000 0.0000 0.0000\n" % (Tab*idnt))<br />
# Looks more logic, because blender use the rotaxis for rot and scale:<br />
file.write("%s*TM_SCALEAXIS %.4f %.4f %.4f\n" % ((Tab*idnt), rota.x, rota.y, rota.z))<br />
file.write("%s*TM_SCALEAXISANG %.4f\n" % ((Tab*idnt), angle))<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mesh_vertexList(file, idnt, verts, count):<br />
<br />
file.write("%s*MESH_VERTEX_LIST {\n" % (Tab*idnt))<br />
<br />
idnt += 1<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing vertices")<br />
<br />
for current_vert in verts:<br />
<br />
vIndex = current_vert.index<br />
<br />
if (vIndex % 1000) == 0:<br />
Blender.Window.DrawProgressBar((vIndex+1.0) / count['vert'], "Writing vertices")<br />
<br />
file.write("%s*MESH_VERTEX %d\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), vIndex, current_vert.co[0], current_vert.co[1], current_vert.co[2]))<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mesh_faceList(file, idnt, me, materials, sGroups, faces, matTable, hasTable, count):<br />
<br />
file.write("%s*MESH_FACE_LIST {\n" % (Tab*idnt))<br />
idnt += 1<br />
faceNo = 0<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing faces")<br />
if hasTable['hasMat'] and matTable:<br />
mats = matTable[hasTable['matRef']]<br />
<br />
fgon_eds = [(ed.key) for ed in me.edges if ed.flag & Mesh.EdgeFlags.FGON]<br />
for current_face in faces:<br />
<br />
face_verts = current_face.verts<br />
smooth = '*MESH_SMOOTHING'<br />
matID = '*MESH_MTLID 0'<br />
<br />
if (faceNo % 500) == 0:<br />
Blender.Window.DrawProgressBar((faceNo+1.0) / count['face'], "Writing faces")<br />
<br />
if hasTable['hasMat']: # Blender mats<br />
#print current_face.mat<br />
mtlid = mats.index(materials[current_face.mat])<br />
matID = '*MESH_MTLID %i' % (mtlid)<br />
<br />
if len(face_verts) is 3:<br />
vert0 = face_verts[0].index<br />
vert1 = face_verts[1].index<br />
vert2 = face_verts[2].index<br />
<br />
#Find hidden (fgon) edges<br />
edge_keys = current_face.edge_keys<br />
eds_fgon = [1,1,1]<br />
for i,ed_key in enumerate(edge_keys):<br />
if ed_key in fgon_eds:<br />
eds_fgon[i] = 0<br />
<br />
#Find Smoothgroups for this face:<br />
if guiTable['VG2SG'] and hasTable['hasSG'] and current_face.smooth:<br />
if sGroups.has_key(vert0) and sGroups.has_key(vert1) and sGroups.has_key(vert2):<br />
sg = []<br />
gis = [sGroups[vert0],sGroups[vert1],sGroups[vert2]]<br />
for gil in gis:<br />
for gi in gil:<br />
sg.append(gi)<br />
sg = set(sg)<br />
for gi in sg:<br />
smooth += ' %s,' % gi<br />
smooth = smooth[:-1]<br />
<br />
elif current_face.smooth:<br />
smooth += ' 1'<br />
<br />
file.write("%s*MESH_FACE %i: A: %i B: %i C: %i AB: %i BC: %i CA: %i\t %s \t%s\n" % ((Tab*idnt), faceNo, vert0, vert1, vert2, eds_fgon[0], eds_fgon[1], eds_fgon[2], smooth, matID))<br />
faceNo+=1<br />
<br />
elif len(face_verts) is 4:<br />
vert0 = face_verts[0].index<br />
vert1 = face_verts[1].index<br />
vert2 = face_verts[2].index<br />
vert3 = face_verts[3].index<br />
<br />
#Find hidden (fgon) edges<br />
edge_keys = current_face.edge_keys<br />
eds_fgon = [1,1,1,1]<br />
for i,ed_key in enumerate(edge_keys):<br />
if ed_key in fgon_eds:<br />
eds_fgon[i] = 0<br />
<br />
#Find Smooth for this face:<br />
if guiTable['VG2SG'] and hasTable['hasSG'] and current_face.smooth:<br />
if sGroups.has_key(vert0) and sGroups.has_key(vert1) and sGroups.has_key(vert2) and sGroups.has_key(vert3):<br />
## I hate VG2SG ;> not sure which way is correct<br />
#sg0,sg1,sg2,sg3 = sGroups[vert0],sGroups[vert1],sGroups[vert2],sGroups[vert3]<br />
#if sg0 == sg1 == sg2 == sg3:<br />
# sg = sg0<br />
#else:<br />
# lens = [len(sg0),len(sg1),len(sg2),len(sg3)]<br />
# lens_sort = lens<br />
# lens_sort.sort()<br />
# lowest = lens_sort[0]<br />
# for l,s in zip(lens,[sg0,sg1,sg2,sg2]):<br />
# if l == lowest:<br />
# sg = s<br />
# break<br />
<br />
sg = []<br />
gis = [sGroups[vert0],sGroups[vert1],sGroups[vert2],sGroups[vert3]]<br />
for gil in gis:<br />
for gi in gil:<br />
sg.append(gi)<br />
sg = set(sg)<br />
for gi in sg:<br />
smooth += ' %s,' % gi<br />
smooth = smooth[:-1]<br />
else:<br />
smooth += ' 1'<br />
<br />
elif current_face.smooth:<br />
smooth += ' 1'<br />
<br />
file.write("%s*MESH_FACE %i: A: %i B: %i C: %i AB: %i BC: %i CA: 0\t %s \t%s\n" % ((Tab*idnt), faceNo, vert0, vert1, vert2, eds_fgon[0], eds_fgon[1], smooth, matID))<br />
faceNo+=1<br />
file.write("%s*MESH_FACE %i: A: %i B: %i C: %i AB: %i BC: %i CA: 0\t %s \t%s\n" % ((Tab*idnt), faceNo, vert0, vert2, vert3, eds_fgon[1], eds_fgon[2], smooth, matID))<br />
faceNo+=1<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
def mesh_tVertList(file, idnt, faces, UVTable, count):<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Setup UV index")<br />
<br />
for current_face in faces:<br />
faceuv = current_face.uv<br />
for current_uv in faceuv:<br />
uv = (current_uv.x, current_uv.y)<br />
if not UVTable.has_key(uv):<br />
UVTable[uv] = 0<br />
count['UVs'] += 1<br />
<br />
#count['UVs'] = len(UVTable)<br />
file.write("%s*MESH_NUMTVERTEX %d\n" % ((Tab*idnt), count['UVs']))<br />
file.write("%s*MESH_TVERTLIST {\n" % (Tab*idnt))<br />
<br />
idnt += 1<br />
Blender.Window.DrawProgressBar(0.0, "Writing UV index")<br />
<br />
for index,current_UV in enumerate(UVTable.iterkeys()):<br />
if (index % 1000) == 0:<br />
Blender.Window.DrawProgressBar((index+1.0) / count['face'], "Writing UV index")<br />
<br />
file.write("%s*MESH_TVERT %i\t%.4f\t%.4f\t0.0000\n" % ((Tab*idnt), index, current_UV[0], current_UV[1]))<br />
UVTable[current_UV] = index<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mesh_tFaceList(file, idnt, faces, UVTable, count):<br />
<br />
tfaceNo = 0<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing Face UV")<br />
<br />
file.write("%s*MESH_NUMTVFACES %i\n" % ((Tab*idnt), count['face']))<br />
file.write("%s*MESH_TFACELIST {\n" % (Tab*idnt))<br />
<br />
idnt += 1<br />
<br />
for current_face in faces:<br />
<br />
faceUV = current_face.uv<br />
<br />
if (tfaceNo % 1000) == 0:<br />
Blender.Window.DrawProgressBar((tfaceNo+1.0) / count['face'], "Writing Face UV")<br />
<br />
if len(faceUV) is 3: #tri<br />
UV0 = UVTable[(faceUV[0].x, faceUV[0].y)]<br />
UV1 = UVTable[(faceUV[1].x, faceUV[1].y)]<br />
UV2 = UVTable[(faceUV[2].x, faceUV[2].y)]<br />
file.write("%s*MESH_TFACE %i\t%i\t%i\t%d\n" % ((Tab*idnt), tfaceNo, UV0, UV1, UV2))<br />
tfaceNo+=1<br />
<br />
elif len(faceUV) is 4: #quad<br />
UV0 = UVTable[(faceUV[0].x, faceUV[0].y)]<br />
UV1 = UVTable[(faceUV[1].x, faceUV[1].y)]<br />
UV2 = UVTable[(faceUV[2].x, faceUV[2].y)]<br />
UV3 = UVTable[(faceUV[3].x, faceUV[3].y)]<br />
file.write("%s*MESH_TFACE %i\t%i\t%i\t%i\n" % ((Tab*idnt), tfaceNo, UV0, UV1, UV2))<br />
tfaceNo+=1<br />
file.write("%s*MESH_TFACE %i\t%i\t%i\t%i\n" % ((Tab*idnt), tfaceNo, UV0, UV2, UV3))<br />
tfaceNo+=1<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mesh_cVertList(file, idnt, faces, cVertTable, count):<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Setup VCol index")<br />
<br />
for current_face in faces:<br />
facecol = current_face.col<br />
for current_col in facecol:<br />
col = (current_col.r, current_col.g, current_col.b)<br />
if not cVertTable.has_key(col):<br />
cVertTable[col] = 0<br />
count['cVert'] += 1<br />
<br />
file.write("%s*MESH_NUMCVERTEX %i\n" % ((Tab*idnt), count['cVert']))<br />
file.write("%s*MESH_CVERTLIST {\n" % (Tab*idnt))<br />
<br />
idnt += 1<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing VCol index")<br />
<br />
for index,current_cvert in enumerate(cVertTable.iterkeys()):<br />
if (index % 1000) == 0:<br />
Blender.Window.DrawProgressBar((index+1.0) / count['face'], "Writing VCol index")<br />
<br />
file.write("%s*MESH_VERTCOL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), index, (current_cvert[0]/256.), (current_cvert[1]/256.), (current_cvert[2]/256.)))<br />
cVertTable[current_cvert] = index<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mesh_cFaceList(file, idnt, faces, cVertTable, count):<br />
<br />
cFaceNo = 0<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing Face Colors")<br />
<br />
file.write("%s*MESH_NUMCFACES %i\n" % ((Tab*idnt), count['face']))<br />
file.write("%s*MESH_CFACELIST {\n" % (Tab*idnt))<br />
<br />
idnt += 1<br />
for current_face in faces:<br />
<br />
if (cFaceNo % 500) == 0:<br />
Blender.Window.DrawProgressBar((cFaceNo+1.0) / count['face'], "Writing Face Colors")<br />
<br />
if len(current_face.verts) is 3: #tri<br />
color0 = cVertTable[(current_face.col[0].r, current_face.col[0].g, current_face.col[0].b)]<br />
color1 = cVertTable[(current_face.col[1].r, current_face.col[1].g, current_face.col[1].b)]<br />
color2 = cVertTable[(current_face.col[2].r, current_face.col[2].g, current_face.col[2].b)]<br />
<br />
file.write("%s*MESH_CFACE %i\t%i\t%i\t%i\n" % ((Tab*idnt), cFaceNo, color0, color1, color2))<br />
cFaceNo+= 1<br />
<br />
elif len(current_face.verts) is 4: #quad<br />
color0 = cVertTable[(current_face.col[0].r, current_face.col[0].g, current_face.col[0].b)]<br />
color1 = cVertTable[(current_face.col[1].r, current_face.col[1].g, current_face.col[1].b)]<br />
color2 = cVertTable[(current_face.col[2].r, current_face.col[2].g, current_face.col[2].b)]<br />
color3 = cVertTable[(current_face.col[3].r, current_face.col[3].g, current_face.col[3].b)]<br />
<br />
file.write("%s*MESH_CFACE %i\t%i\t%i\t%i\n" % ((Tab*idnt), cFaceNo, color0, color1, color2))<br />
cFaceNo+= 1<br />
file.write("%s*MESH_CFACE %i\t%i\t%i\t%i\n" % ((Tab*idnt), cFaceNo, color0, color2, color3))<br />
cFaceNo+= 1<br />
<br />
idnt -= 1<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
<br />
def mesh_normals(file, idnt, faces, verts, count):<br />
# To export quads it is needed to calculate all face and vertex normals new!<br />
vec_null = Blender.Mathutils.Vector(0.0, 0.0, 0.0)<br />
v_normals = dict([(v.index, vec_null) for v in verts])<br />
f_normals = dict([(f.index, vec_null) for f in faces])<br />
f_normals_quad = {}<br />
<br />
file.write("%s*MESH_NORMALS {\n" % (Tab*idnt))<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Setup Normals")<br />
<br />
#-- Calculate new face and vertex normals<br />
<br />
for i,f in enumerate(faces):<br />
f_dic = f_normals[i]<br />
f_vec = f_dic[0]<br />
<br />
f_verts = f.verts<br />
<br />
if len(f_verts) is 3: #tri<br />
v0,v1,v2 = f_verts[:]<br />
v0_i,v1_i,v2_i = f_verts[0].index, f_verts[1].index, f_verts[2].index<br />
f_no = Blender.Mathutils.TriangleNormal(v0.co, v1.co, v2.co)<br />
f_normals[f.index] = f_no<br />
if f.smooth:<br />
v_normals[v0_i] = v_normals[v0_i] + f_no<br />
v_normals[v1_i] = v_normals[v1_i] + f_no<br />
v_normals[v2_i] = v_normals[v2_i] + f_no<br />
<br />
if len(f_verts) is 4: #quad<br />
v0,v1,v2,v3 = f_verts[:]<br />
v0_i,v1_i,v2_i,v3_i = f_verts[0].index, f_verts[1].index, f_verts[2].index,f_verts[3].index<br />
f_no0 = Blender.Mathutils.TriangleNormal(v0.co, v1.co, v2.co)<br />
f_no1 = Blender.Mathutils.TriangleNormal(v2.co, v3.co, v0.co)<br />
f_normals[f.index] = f_no0<br />
f_normals_quad[f.index] = f_no1<br />
if f.smooth:<br />
v_normals[v0_i] = v_normals[v0_i] + f_no0<br />
v_normals[v1_i] = v_normals[v1_i] + f_no0<br />
v_normals[v2_i] = v_normals[v2_i] + f_no0<br />
<br />
v_normals[v0_i] = v_normals[v2_i] + f_no1<br />
v_normals[v2_i] = v_normals[v3_i] + f_no1<br />
v_normals[v3_i] = v_normals[v0_i] + f_no1<br />
<br />
<br />
#-- Normalize vectors<br />
#for i,vec in v_normals.iteritems():<br />
for vec in v_normals.itervalues():<br />
vec.normalize()<br />
<br />
#-- Finally write normals<br />
normNo = 0<br />
idnt += 2<br />
<br />
Blender.Window.DrawProgressBar(0.0, "Writing Normals")<br />
<br />
for f in faces:<br />
<br />
if (normNo % 500) == 0:<br />
Blender.Window.DrawProgressBar((normNo+1.0) / count['face'], "Writing Normals")<br />
<br />
f_verts = f.verts<br />
smooth = f.smooth<br />
<br />
if len(f_verts) is 3: #tri<br />
v0_i = f_verts[0].index<br />
v1_i = f_verts[1].index<br />
v2_i = f_verts[2].index<br />
<br />
idnt -= 1<br />
f_no = f_normals[f.index]<br />
file.write("%s*MESH_FACENORMAL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), normNo, f_no.x, f_no.y, f_no.z))<br />
normNo += 1<br />
<br />
idnt += 1<br />
mesh_vertNorm(file, idnt, v0_i, v1_i, v2_i, v_normals, smooth, f_no)<br />
<br />
#elif len(f_verts) is 4: #quad<br />
if len(f_verts) is 4: #quad<br />
v0_i = f_verts[0].index<br />
v1_i = f_verts[1].index<br />
v2_i = f_verts[2].index<br />
v3_i = f_verts[3].index<br />
<br />
idnt -= 1<br />
f_no = f_normals[f.index]<br />
file.write("%s*MESH_FACENORMAL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), normNo, f_no0.x, f_no0.y, f_no0.z))<br />
normNo += 1<br />
<br />
idnt += 1<br />
mesh_vertNorm(file, idnt, v0_i, v1_i, v2_i, v_normals, smooth, f_no0)<br />
<br />
idnt -= 1<br />
f_no = f_normals_quad[f.index]<br />
file.write("%s*MESH_FACENORMAL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), normNo, f_no1.x, f_no1.y, f_no1.z))<br />
normNo += 1<br />
<br />
idnt += 1<br />
mesh_vertNorm(file, idnt, v0_i, v2_i, v3_i, v_normals, smooth, f_no1)<br />
<br />
<br />
idnt -= 2<br />
file.write("%s}\n" % (Tab*idnt))<br />
<br />
def mesh_vertNorm(file, idnt, v0_i, v1_i, v2_i, v_normals, smooth, f_no):<br />
if smooth:<br />
v_no0 = v_normals[v0_i]<br />
v_no1 = v_normals[v1_i]<br />
v_no2 = v_normals[v2_i]<br />
else: #If solid use the face normal<br />
v_no0 = v_no1 = v_no2 = f_no<br />
<br />
file.write("%s*MESH_VERTEXNORMAL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), v0_i, v_no0.x, v_no0.y, v_no0.z))<br />
file.write("%s*MESH_VERTEXNORMAL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), v1_i, v_no1.x, v_no1.y, v_no1.z))<br />
file.write("%s*MESH_VERTEXNORMAL %i\t%.4f\t%.4f\t%.4f\n" % ((Tab*idnt), v2_i, v_no2.x, v_no2.y, v_no2.z))<br />
<br />
<br />
def mesh_footer(file, idnt, hasTable):<br />
<br />
file.write("%s*PROP_MOTIONBLUR 0\n" % (Tab*idnt))<br />
file.write("%s*PROP_CASTSHADOW 1\n" % (Tab*idnt))<br />
file.write("%s*PROP_RECVSHADOW 1\n" % (Tab*idnt))<br />
<br />
if hasTable['hasMat'] != 0:<br />
file.write("%s*MATERIAL_REF %i\n" % ((Tab*idnt), hasTable['matRef']))<br />
<br />
#-------------------------End?----------------------<br />
<br />
<br />
def write_ui(filename):<br />
<br />
global guiTable, EXPORT_MOD, EXPORT_MTL, EXPORT_UV, EXPORT_VC, EXPORT_SELO, EXPORT_UVI, EXPORT_VG2SG<br />
guiTable = {'MOD': 1, 'MTL': 1, 'UV': 1, 'VC': 1, 'SELO': 1, 'UVI': 0, 'VG2SG': 1, 'RECENTER':0}<br />
<br />
EXPORT_MOD = Draw.Create(guiTable['MOD'])<br />
EXPORT_MTL = Draw.Create(guiTable['MTL'])<br />
EXPORT_UV = Draw.Create(guiTable['UV'])<br />
EXPORT_VC = Draw.Create(guiTable['VC'])<br />
EXPORT_SELO = Draw.Create(guiTable['SELO'])<br />
EXPORT_VG2SG = Draw.Create(guiTable['VG2SG'])<br />
EXPORT_REC = Draw.Create(guiTable['RECENTER'])<br />
<br />
# Get USER Options<br />
pup_block = [('Mesh Options...'),('Apply Modifiers', EXPORT_MOD, 'Use modified mesh data from each object.'),('Materials', EXPORT_MTL, 'Export Materials.'),('Face UV', EXPORT_UV, 'Export texface UV coords.'),('Vertex Colors', EXPORT_VC, 'Export vertex colors'),('Context...'),('Selection Only', EXPORT_SELO, 'Only export objects in visible selection, else export all mesh object.'),('Bonus...'),('VertGr. as SmoothGr.', EXPORT_VG2SG, 'Make SmoothGroups by VertGroups. See doc.'), ('Center Objects', EXPORT_REC, 'Center ALL objects to World-Grid-Origin-Center-Point-(0,0,0). ;)')]<br />
<br />
if not Draw.PupBlock('Export...', pup_block):<br />
return<br />
<br />
Window.WaitCursor(1)<br />
<br />
guiTable['MOD'] = EXPORT_MOD.val<br />
guiTable['MTL'] = EXPORT_MTL.val<br />
guiTable['UV'] = EXPORT_UV.val<br />
guiTable['VC'] = EXPORT_VC.val<br />
guiTable['SELO'] = EXPORT_SELO.val<br />
guiTable['VG2SG'] = EXPORT_VG2SG.val<br />
guiTable['RECENTER'] = EXPORT_REC.val<br />
<br />
if not filename.lower().endswith('.ase'):<br />
filename += '.ase'<br />
<br />
write(filename)<br />
<br />
Window.WaitCursor(0)<br />
<br />
<br />
if __name__ == '__main__':<br />
Window.FileSelector(write_ui, 'Export ASCII Scene', sys.makename(ext='.ase'))<br />
<br />
</pre></div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=Using_Blender_for_Doom_3_Modeling&diff=6828Using Blender for Doom 3 Modeling2008-08-23T09:03:36Z<p>OrbWeaver: Added links to new pages for Blender ASE export scripts</p>
<hr />
<div>Posted originally by Magnesius:<br />
<br />
I am quite happy with Blender, and I would recommend it to everyone. Like with all more complex applications, you have to be ambitious until you get your first results.<br />
<br />
# Start with http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro. Although this wiki book has a badly chosen title, the beginning is helpful when you make your first steps with Blender. But beware - Blender is not really intuitive at the beginning. However, once you learned all the keyboard shortcuts, you'll quickly get into the workflow. I followed this tutorial up to the animation part of the Gingerbread man, and continued with...<br />
# http://blender3d.org/cms/Tutorials.243.0.html (esp. the "materials"-part is important). This is the official tutorial list, search there first when you want to learn something (e.g. animating a mesh). Take a look at several tutorials there, and extend your skills a bit.<br />
# Once you have created your first cube, your first vase and so on with Blender, you might want to export it to Doom.<br />
<br />
These tutorials are important, read them carefully:<br />
* http://www.katsbits.com/htm/tutorials/doom_3_prepping_models.htm<br />
* http://www.katsbits.com/htm/tutorials/doom_3_smooth_groups.htm<br />
<br />
Using Goofos ASE Exporter instead of the proposed USM Exporter, you can export multiple objects at once, you do not have to merge them. Get the script here or using the link below:<br />
<br />
http://www.doom3world.org/phpbb2/viewtopic.php?t=9275<br />
<br />
==Resources for Blender users==<br />
<br />
* [[GoofosASEExporter | Snapshot of the ASE export script for Blender]] -- currently version 0.6.10<br />
* [[InstallASEScript | ASE installer script]] -- a small convenience script for installing an ASE file into a given location (i.e. under the models/ hierarchy) while automatically making texture name replacements from a list in a separate file.<br />
<br />
[[Category:Models]]</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_-_Compiling_in_Linux&diff=6726DarkRadiant - Compiling in Linux2008-08-06T12:58:54Z<p>OrbWeaver: /* Compile */</p>
<hr />
<div>== Install Libraries and Tools ==<br />
<br />
=== Ubuntu 7.04 ===<br />
<br />
You'll need a bunch of packages to get DarkRadiant to compile without glitches. I can't remember all from the top of my head, I will try to complete this list during my next Linux setup, but don't hold your breath. These are the names as found in Ubuntu 7.04 (Feisty Fawn):<br />
<br />
* g++<br />
* gcc-4.1<br />
* scons 0.96.93 (or higher)<br />
* libboost-dev<br />
* libboost-serialization-dev<br />
* libboost-filesystem-dev<br />
* libboost-regex-dev<br />
* libglew-dev<br />
* libgtk2.0-dev<br />
* libglib2.0-dev (maybe installed with libgtk2.0-dev)<br />
* libgtkglext1-dev<br />
* libxml2-dev<br />
* libopenal-dev<br />
* libalut-dev<br />
* libvorbis-dev<br />
<br />
Always download all the dependencies as well. Python should already be installed by Ubuntu at least, other distributions might require to install it separately.<br />
<br />
If you get an error about the G++ compiler not being found, make sure you also install the <code>g++</code> package, and not just the <code>g++-4.1</code> package.<br />
<br />
=== Ubuntu 7.10 ===<br />
* g++<br />
* scons 0.97<br />
* libboost-dev<br />
* libboost-regex-dev<br />
* libgtk2.0-dev<br />
* libglew1.4-dev<br />
* libgtkglext1-dev<br />
* libxml2-dev<br />
* libboost-serialization-dev<br />
* libboost-filesystem-dev<br />
* libboost-date-time-dev<br />
* libalut-dev<br />
* libvorbis-dev<br />
<br />
Always download all the dependencies as well.<br />
<br />
For the D3Hook plugin, the boost/asio library is needed (version 0.3.7, newer versions don't work). This library is located in the plugins/dm.d3hook/boost/ folder and should be ready for compilation.<br />
<br />
=== Ubuntu 8.04 ===<br />
Copy and paste the following into a terminal:<br />
<br />
<code>sudo apt-get install g++ scons libboost-dev libboost-regex-dev libgtk2.0-dev libglew1.5-dev libgtkglext1-dev libxml2-dev libboost-serialization-dev libboost-filesystem-dev libboost-date-time-dev libalut-dev libvorbis-dev</code><br />
<br />
In the terminal, Ctrl Shift V is used for pasting instead of Ctrl V.<br />
<br />
== Obtain the source ==<br />
<br />
Install the subversion client. On Ubuntu this can be done via the Synaptic Manager or the command line:<br />
sudo apt-get install subversion<br />
<br />
Change to the directory where you want the source code to be in and then check the current version out of SVN with:<br />
<br />
svn checkout https://darkradiant.svn.sourceforge.net/svnroot/darkradiant/trunk/darkradiant/ darkradiant<br />
<br />
== Compile ==<br />
<br />
'''Note: in the past, DarkRadiant was built using SCons. This build system has now been deprecated in favour of the standard Automake build, and in future the SCons files will be removed. Please do not expect any support from the developers if you attempt to compile DarkRadiant using the SCons build system.'''<br />
<br />
DarkRadiant employs an Autoconf/Automake build system under Linux as used in the majority of open-source projects. Compilation can be achieved using the standard<br />
<br />
$ ./configure<br />
$ make<br />
$ make install<br />
<br />
on most systems.<br />
<br />
By default, a release build will be created; if a debug build is required pass the '''--enable-debug''' option to the configure script.<br />
<br />
Other points to note about the configure process:<br />
<br />
* The DarkMod-specific plugins are not built by default. To enable these, pass '''--enable-darkmod-plugins''' to ./configure.<br />
* The configure script autodetects required dependencies, and will conditionally-enable optional components of DarkRadiant (such as the sound plugin) based on what it finds.<br />
* For quick testing of a DarkRadiant build, it is desirable to install it into a temporary location rather than the default of '''/usr/local'''; for this, simply pass a prefix option such as '''--prefix=/tmp/dr''' to configure, after which DarkRadiant will be installed in '''/tmp/dr/bin/darkradiant'''.<br />
* To build a Debian/Ubuntu package, simply run '''dpkg-buildpackage -rfakeroot''' in the main '''darkradiant''' directory. The .deb will be created in the parent directory.<br />
<br />
If you want to update the source with the latest version from the SVN, just issue:<br />
<br />
svn update<br />
<br />
in the darkradiant source directory. <code>svn info</code> shows you what version you currently have.<br />
<br />
= Note for users with Multiprocessor Systems =<br />
Scons can run several jobs simultaneously, which can use your multicore system to its full capacity. By calling<br />
<br />
scons JOBS="2"<br />
<br />
the tool runs up to two compile tasks at the same time, which speeds up things a bit.<br />
<br />
For a dual-core setup, a value of 2 jobs should be is sufficient, especially if you have only one Gbyte of memory, or less. If your machine has 2 or more Gbyte, you can also try higher values like:<br />
<br />
scons JOBS="4"<br />
<br />
== See also ==<br />
* [[DarkRadiant Linux Issues]]<br />
<br />
{{coding}}<br />
{{darkradiant|sort=Compilation}}</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_pre-release_tests&diff=2152DarkRadiant pre-release tests2007-01-14T20:56:35Z<p>OrbWeaver: /* Map handling */</p>
<hr />
<div>The following '''pre-release tests''' (UAT) should be performed before a new DarkRadiant release is uploaded to SourceForge, to ensure that no regressions of functionality have occured during development.<br />
<br />
Before performing these tests, the latest version of the Mod should be updated from CVS.<br />
<br />
===Map handling===<br />
# Load a large map, like askave or mansion_alpha. The application should not crash or go into an infinite loop. When the map is loaded, it should be displayed correctly, without anything obviously missing.<br />
# Save the large map to a new test file, then load another large map. Reload the newly-saved first map. All maps must load correctly.<br />
# Enter Doom 3 and compile (dmap) the newly-saved large map from the previous step. The map must compile and be playable (assuming that the original map was compilable).<br />
# Zoom and drag the 2D views around, to view all parts of the map. The map contents must not disappear without warning at any time.<br />
# On the Filters menu, select some filters, such as World Geometry or All Entities. The appropriate objects should disappear from the 2D views.<br />
<br />
===Textures===<br />
# Open up the Media Browser, and look at some texture folders. There should be a reasonable number of textures populated, especially in large folders such as textures/base_wall.<br />
<br />
===Rendering===<br />
# Create a new map. Add a brush, and texture it. Add a light in such a position as to illuminate this brush, and enter lighting mode (F3). The brush must be illuminated by the light.<br />
<br />
===Models and entities===<br />
# Use the Entity Chooser to create an AI-based entity. It should load and be displayed in wireframe mode in the 2D windows. Navigate the camera and inspect the AI; its textures should be displayed (assuming the selected AI has valid textures associated with it).<br />
# Use the Model Selector to browse the list of models. Click on a few to display them in the preview -- they should display correctly with their textures. Some of the models should have skins available, click on these to ensure that the model is updated with the new skin. Click OK to insert a model into the map. It should be displayed in wireframe view and in the camera view with correct textures.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_pre-release_tests&diff=2151DarkRadiant pre-release tests2007-01-14T20:55:29Z<p>OrbWeaver: /* AI and entities */</p>
<hr />
<div>The following '''pre-release tests''' (UAT) should be performed before a new DarkRadiant release is uploaded to SourceForge, to ensure that no regressions of functionality have occured during development.<br />
<br />
Before performing these tests, the latest version of the Mod should be updated from CVS.<br />
<br />
===Map handling===<br />
# Load a large map, like askave or mansion_alpha. The application should not crash or go into an infinite loop. When the map is loaded, it should be displayed correctly, without anything obviously missing.<br />
# Save the large map to a new test file, then load another large map. Reload the newly-saved first map. All maps must load correctly.<br />
# Enter Doom 3 and compile (dmap) the newly-saved large map from the previous step. The map must compile and be playable (assuming that the original map was compilable).<br />
<br />
===Textures===<br />
# Open up the Media Browser, and look at some texture folders. There should be a reasonable number of textures populated, especially in large folders such as textures/base_wall.<br />
<br />
===Rendering===<br />
# Create a new map. Add a brush, and texture it. Add a light in such a position as to illuminate this brush, and enter lighting mode (F3). The brush must be illuminated by the light.<br />
<br />
===Models and entities===<br />
# Use the Entity Chooser to create an AI-based entity. It should load and be displayed in wireframe mode in the 2D windows. Navigate the camera and inspect the AI; its textures should be displayed (assuming the selected AI has valid textures associated with it).<br />
# Use the Model Selector to browse the list of models. Click on a few to display them in the preview -- they should display correctly with their textures. Some of the models should have skins available, click on these to ensure that the model is updated with the new skin. Click OK to insert a model into the map. It should be displayed in wireframe view and in the camera view with correct textures.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_pre-release_tests&diff=2150DarkRadiant pre-release tests2007-01-14T20:52:46Z<p>OrbWeaver: </p>
<hr />
<div>The following '''pre-release tests''' (UAT) should be performed before a new DarkRadiant release is uploaded to SourceForge, to ensure that no regressions of functionality have occured during development.<br />
<br />
Before performing these tests, the latest version of the Mod should be updated from CVS.<br />
<br />
===Map handling===<br />
# Load a large map, like askave or mansion_alpha. The application should not crash or go into an infinite loop. When the map is loaded, it should be displayed correctly, without anything obviously missing.<br />
# Save the large map to a new test file, then load another large map. Reload the newly-saved first map. All maps must load correctly.<br />
# Enter Doom 3 and compile (dmap) the newly-saved large map from the previous step. The map must compile and be playable (assuming that the original map was compilable).<br />
<br />
===Textures===<br />
# Open up the Media Browser, and look at some texture folders. There should be a reasonable number of textures populated, especially in large folders such as textures/base_wall.<br />
<br />
===Rendering===<br />
# Create a new map. Add a brush, and texture it. Add a light in such a position as to illuminate this brush, and enter lighting mode (F3). The brush must be illuminated by the light.<br />
<br />
===AI and entities===<br />
# Use the Entity Chooser to create an AI-based entity. It should load and be displayed in wireframe mode in the 2D windows. Navigate the camera and inspect the AI; its textures should be displayed (assuming the selected AI has valid textures associated with it).</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_pre-release_tests&diff=2149DarkRadiant pre-release tests2007-01-14T20:49:13Z<p>OrbWeaver: initial</p>
<hr />
<div>The following '''pre-release tests''' (UAT) should be performed before a new DarkRadiant release is uploaded to SourceForge, to ensure that no regressions of functionality have occured during development.<br />
<br />
===Map handling===<br />
# Load a large map, like askave or mansion_alpha. The application should not crash or go into an infinite loop. When the map is loaded, it should be displayed correctly, without anything obviously missing.<br />
# Save the large map to a new test file, then load another large map. Reload the newly-saved first map. All maps must load correctly.<br />
# Enter Doom 3 and compile (dmap) the newly-saved large map from the previous step. The map must compile and be playable (assuming that the original map was compilable).<br />
<br />
===Rendering===<br />
# Create a new map. Add a brush, and texture it. Add a light in such a position as to illuminate this brush, and enter lighting mode (F3). The brush must be illuminated by the light.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant&diff=2148DarkRadiant2007-01-14T20:45:24Z<p>OrbWeaver: /* Developers */</p>
<hr />
<div>DarkRadiant is The Dark Mod's very own map editor, based on the open-source GTKRadiant. <br />
<br />
Orbweaver started working on it in Spring 2006, the initial release was version 0.5.0. Features at this stage include a categorised entity inspector, drag-resizable lights, color selection tool for lights and numerous bugfixes over the GtkRadiant codebase.<br />
<br />
* [[DarkRadiant - Quick Installation Guide]]<br />
* The codebase is stored on sourceforge SVN: http://sourceforge.net/projects/darkradiant/<br />
* [[DarkRadiant - Compilation Problems]]<br />
<br />
==Developers==<br />
<br />
* [[DarkRadiant coding standards]]<br />
* [[DarkRadiant module system]]<br />
* [[DarkRadiant render system]]<br />
* [[DarkRadiant scene graph]]<br />
* [[DarkRadiant pre-release tests]]<br />
<br />
[[Category:DarkRadiant]]</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2126DarkRadiant coding standards2006-12-31T18:00:10Z<p>OrbWeaver: /* Naming conventions */</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, the style of legacy GTKRadiant code should ''not'' be used as an example for new code. Therefore these '''coding standards''' should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
===Typedefs===<br />
Using the typedef keyword can improve readability and save typing by providing a simple name for a more complex data structure, however inappropriate use can also reduce readibility by forcing the reader to look up numerous typedefs which do no more than rename a standard type.<br />
<br />
For example:<br />
<br />
typedef std::vector<std::string> StringList<br />
<br />
is a good typedef, because it is obvious what the type means, it allows easy editing if it were required to change it to a std::list rather than a vector (for example), and it makes it easy to work with derived types ('''StringList::iterator''' rather than '''std::vector<std::string>::iterator''').<br />
<br />
On the other hand,<br />
<br />
typedef float ShaderScaleParameter<br />
<br />
is a bad typedef. It provides no improved readability over simply using the primitive type, and gives the impression to a reader unfamiliar with the code that a more complex data structure is being used.<br />
<br />
* Always name typedefs based on what the type is, not what it is for. Use "StringList" rather than "ShaderParameterList" or "ArgumentList".<br />
* Never use typedefs to rename a standard type (unless there is a specific purpose, like providing a standard public typedef as part of a functor interface).<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Never use global variables.<br />
<br />
===Data types and objects===<br />
<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Do not use '''const char*''' objects except where necessary to pass to a library function (such as the C-based GTK library), or where a string constant is needed. Functions that accept or return strings should use '''std::string''' instead.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods. This is an important part of separating interface from implementation, since a get/set method might actually modify more than one member variable, or even perform a calculation without referencing members at all.<br />
<br />
===Comments===<br />
<br />
Exactly where and how to place comments is a matter of personal preference. However, at the very least, ''all'' public methods and functions should be commented in their associated header file, with details of their parameters, return value and function, along with any other important information (such as prerequisites for calling the function, who is responsible for destroying returned data etc.).</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_render_system&diff=2085DarkRadiant render system2006-11-24T21:41:12Z<p>OrbWeaver: Basic renderer information</p>
<hr />
<div>The [[DarkRadiant]] '''render system''' works in two phases.<br />
<br />
* '''Front end (sorting phase)'''. At this stage, the scene graph is traversed, and entities declared as '''Renderable''' are required to submit their render information to a '''Renderer''' which, despite the name, performs no rendering but is merely responsible for sorting renderable objects into an appropriate structure of state information. Both of these interfaces (Renderable and Renderer) are defined in '''include/renderable.h'''<br />
* '''Back end (rendering phase)'''. Here the actual OpenGL calls are made by calling the render method defined in the '''OpenGLRenderable''' interface on each object previously submitted to the Renderer. This interface is defined in '''include/irender.h'''. This stage is currently handled by the ShaderCache, presumably in order to group geometry by shader and keep OpenGL state changes to a minimum.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant&diff=2084DarkRadiant2006-11-24T21:36:18Z<p>OrbWeaver: /* Developers */</p>
<hr />
<div>DarkRadiant is The Dark Mod's very own map editor, based on the open-source GTKRadiant. <br />
<br />
Orbweaver started working on it in Spring 2006, the initial release was version 0.5.0. Features at this stage include a categorised entity inspector, drag-resizable lights, color selection tool for lights and numerous bugfixes over the GtkRadiant codebase.<br />
<br />
* [[DarkRadiant - Quick Installation Guide]]<br />
* The codebase is stored on sourceforge SVN: http://sourceforge.net/projects/darkradiant/<br />
* [[DarkRadiant - Compilation Problems]]<br />
<br />
==Developers==<br />
<br />
* [[DarkRadiant coding standards]]<br />
* [[DarkRadiant module system]]<br />
* [[DarkRadiant render system]]<br />
<br />
[[Category:DarkRadiant]]</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2077DarkRadiant coding standards2006-11-23T21:51:41Z<p>OrbWeaver: /* Module interface */</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, the style of legacy GTKRadiant code should ''not'' be used as an example for new code. There following '''coding standards''' should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Never use global variables.<br />
<br />
===Data types and objects===<br />
<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Do not use '''const char*''' objects except where necessary to pass to a library function (such as the C-based GTK library), or where a string constant is needed. Functions that accept or return strings should use '''std::string''' instead.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods. This is an important part of separating interface from implementation, since a get/set method might actually modify more than one member variable, or even perform a calculation without referencing members at all.<br />
<br />
===Comments===<br />
<br />
Exactly where and how to place comments is a matter of personal preference. However, at the very least, ''all'' public methods and functions should be commented in their associated header file, with details of their parameters, return value and function, along with any other important information (such as prerequisites for calling the function, who is responsible for destroying returned data etc.).</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant&diff=2076DarkRadiant2006-11-23T21:51:30Z<p>OrbWeaver: /* Developers */</p>
<hr />
<div>DarkRadiant is The Dark Mod's very own map editor, based on the open-source GTKRadiant. <br />
<br />
Orbweaver started working on it in Spring 2006, the initial release was version 0.5.0. Features at this stage include a categorised entity inspector, drag-resizable lights, color selection tool for lights and numerous bugfixes over the GtkRadiant codebase.<br />
<br />
* [[DarkRadiant - Quick Installation Guide]]<br />
* The codebase is stored on sourceforge SVN: http://sourceforge.net/projects/darkradiant/<br />
* [[DarkRadiant - Compilation Problems]]<br />
<br />
==Developers==<br />
<br />
* [[DarkRadiant coding standards]]<br />
* [[DarkRadiant module system]]<br />
<br />
[[Category:DarkRadiant]]</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2047DarkRadiant coding standards2006-10-29T12:17:24Z<p>OrbWeaver: /* General code structure */</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, the style of legacy GTKRadiant code should ''not'' be used as an example for new code. There following '''coding standards''' should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Never use global variables.<br />
<br />
===Data types and objects===<br />
<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Do not use '''const char*''' objects except where necessary to pass to a library function (such as the C-based GTK library), or where a string constant is needed. Functions that accept or return strings should use '''std::string''' instead.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods. This is an important part of separating interface from implementation, since a get/set method might actually modify more than one member variable, or even perform a calculation without referencing members at all.<br />
<br />
===Comments===<br />
<br />
Exactly where and how to place comments is a matter of personal preference. However, at the very least, ''all'' public methods and functions should be commented in their associated header file, with details of their parameters, return value and function, along with any other important information (such as prerequisites for calling the function, who is responsible for destroying returned data etc.).<br />
<br />
==Module interface==<br />
<br />
The design of DarkRadiant is a modular one. Each module encapsulates a certain amount of functionality, such as the '''entity''' module which provides entity creation and rendering, and the '''model''' module which loads modules from ASE, LWO or MD5 files. Each module implements an interface which is defined in an "i*.h" file in the '''include''' directory (such as "imodel.h" and "ientity.h"). There is also a '''Radiant''' module whose interface is specified in "qerplugin.h".<br />
<br />
Modules access each other using inline functions defined in the interface header file, such as '''GlobalRadiant()''' or '''GlobalShaderCache()'''. These functions return a reference (or pointer) to a static instance of the corresponding module. The initialisation order of modules is important, and this is controlled via the use of a '''ModuleRef''' template, which instantiates and maintains a reference to a singleton module instance, and "Dependencies classes" which are empty classes that inherit from a number of ModuleRef templates, thus ensuring that each ModuleRef is constructed before the Dependencies class itself.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2046DarkRadiant coding standards2006-10-29T12:14:01Z<p>OrbWeaver: </p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, the style of legacy GTKRadiant code should ''not'' be used as an example for new code. There following '''coding standards''' should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Never use global variables.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods. This is an important part of separating interface from implementation, since a get/set method might actually modify more than one member variable, or even perform a calculation without referencing members at all.<br />
<br />
===Comments===<br />
<br />
Exactly where and how to place comments is a matter of personal preference. However, at the very least, ''all'' public methods and functions should be commented in their associated header file, with details of their parameters, return value and function, along with any other important information (such as prerequisites for calling the function, who is responsible for destroying returned data etc.).<br />
<br />
==Module interface==<br />
<br />
The design of DarkRadiant is a modular one. Each module encapsulates a certain amount of functionality, such as the '''entity''' module which provides entity creation and rendering, and the '''model''' module which loads modules from ASE, LWO or MD5 files. Each module implements an interface which is defined in an "i*.h" file in the '''include''' directory (such as "imodel.h" and "ientity.h"). There is also a '''Radiant''' module whose interface is specified in "qerplugin.h".<br />
<br />
Modules access each other using inline functions defined in the interface header file, such as '''GlobalRadiant()''' or '''GlobalShaderCache()'''. These functions return a reference (or pointer) to a static instance of the corresponding module. The initialisation order of modules is important, and this is controlled via the use of a '''ModuleRef''' template, which instantiates and maintains a reference to a singleton module instance, and "Dependencies classes" which are empty classes that inherit from a number of ModuleRef templates, thus ensuring that each ModuleRef is constructed before the Dependencies class itself.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2045DarkRadiant coding standards2006-10-29T12:12:05Z<p>OrbWeaver: /* Access */</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, there are new '''coding standards''' which should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Never use global variables.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods. This is an important part of separating interface from implementation, since a get/set method might actually modify more than one member variable, or even perform a calculation without referencing members at all.<br />
<br />
===Comments===<br />
<br />
Exactly where and how to place comments is a matter of personal preference. However, at the very least, ''all'' public methods and functions should be commented in their associated header file, with details of their parameters, return value and function, along with any other important information (such as prerequisites for calling the function, who is responsible for destroying returned data etc.).<br />
<br />
==Module interface==<br />
<br />
The design of DarkRadiant is a modular one. Each module encapsulates a certain amount of functionality, such as the '''entity''' module which provides entity creation and rendering, and the '''model''' module which loads modules from ASE, LWO or MD5 files. Each module implements an interface which is defined in an "i*.h" file in the '''include''' directory (such as "imodel.h" and "ientity.h"). There is also a '''Radiant''' module whose interface is specified in "qerplugin.h".<br />
<br />
Modules access each other using inline functions defined in the interface header file, such as '''GlobalRadiant()''' or '''GlobalShaderCache()'''. These functions return a reference (or pointer) to a static instance of the corresponding module. The initialisation order of modules is important, and this is controlled via the use of a '''ModuleRef''' template, which instantiates and maintains a reference to a singleton module instance, and "Dependencies classes" which are empty classes that inherit from a number of ModuleRef templates, thus ensuring that each ModuleRef is constructed before the Dependencies class itself.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2044DarkRadiant coding standards2006-10-29T12:10:26Z<p>OrbWeaver: /* General code structure */</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, there are new '''coding standards''' which should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Never use global variables.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods.<br />
<br />
===Comments===<br />
<br />
Exactly where and how to place comments is a matter of personal preference. However, at the very least, ''all'' public methods and functions should be commented in their associated header file, with details of their parameters, return value and function, along with any other important information (such as prerequisites for calling the function, who is responsible for destroying returned data etc.).<br />
<br />
==Module interface==<br />
<br />
The design of DarkRadiant is a modular one. Each module encapsulates a certain amount of functionality, such as the '''entity''' module which provides entity creation and rendering, and the '''model''' module which loads modules from ASE, LWO or MD5 files. Each module implements an interface which is defined in an "i*.h" file in the '''include''' directory (such as "imodel.h" and "ientity.h"). There is also a '''Radiant''' module whose interface is specified in "qerplugin.h".<br />
<br />
Modules access each other using inline functions defined in the interface header file, such as '''GlobalRadiant()''' or '''GlobalShaderCache()'''. These functions return a reference (or pointer) to a static instance of the corresponding module. The initialisation order of modules is important, and this is controlled via the use of a '''ModuleRef''' template, which instantiates and maintains a reference to a singleton module instance, and "Dependencies classes" which are empty classes that inherit from a number of ModuleRef templates, thus ensuring that each ModuleRef is constructed before the Dependencies class itself.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2043DarkRadiant coding standards2006-10-28T09:50:32Z<p>OrbWeaver: </p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, there are new '''coding standards''' which should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Never use global variables.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods.<br />
<br />
==Module interface==<br />
<br />
The design of DarkRadiant is a modular one. Each module encapsulates a certain amount of functionality, such as the '''entity''' module which provides entity creation and rendering, and the '''model''' module which loads modules from ASE, LWO or MD5 files. Each module implements an interface which is defined in an "i*.h" file in the '''include''' directory (such as "imodel.h" and "ientity.h"). There is also a '''Radiant''' module whose interface is specified in "qerplugin.h".<br />
<br />
Modules access each other using inline functions defined in the interface header file, such as '''GlobalRadiant()''' or '''GlobalShaderCache()'''. These functions return a reference (or pointer) to a static instance of the corresponding module. The initialisation order of modules is important, and this is controlled via the use of a '''ModuleRef''' template, which instantiates and maintains a reference to a singleton module instance, and "Dependencies classes" which are empty classes that inherit from a number of ModuleRef templates, thus ensuring that each ModuleRef is constructed before the Dependencies class itself.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2042DarkRadiant coding standards2006-10-28T09:45:56Z<p>OrbWeaver: /* General code structure */</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, there are new '''coding standards''' which should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''. Classes that use these home-grown versions should be modified so as not to use them if at all possible, and eventually they will be removed.<br />
* Never use global variables.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2041DarkRadiant coding standards2006-10-28T09:44:12Z<p>OrbWeaver: </p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, there are new '''coding standards''' which should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Each new class should be contained within its own file pair: '''MyClass''' is contained in '''MyClass.h''' and '''MyClass.cpp'''. This rule does not apply to tightly-bound local classes, such as a functor class which is used by only a single method in a .cpp file. It is also acceptable to define trivial data structures returned by methods in the header file declaring the method, if that data structure is not used elsewhere.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Always use STL or Boost objects, rather than home-grown reinventions of the same thing. Use '''std::string''' not '''CopiedString''', and '''std::map''' in favour of '''HashedCache'''.<br />
* Never use global variables.<br />
<br />
===Static variables===<br />
<br />
If a static variable is required, encapsulate it within a small method that returns a reference to the static, e.g.<br />
MyObject& staticInstance() {<br />
static MyObject _instance;<br />
return _instance;<br />
}<br />
This ensures that the static instance will be initialised when the method is first called, which avoids problems with the undefinability of static initialisation.<br />
<br />
===Access===<br />
<br />
* All data members should be private, except in data structures which provide no methods (other than constructors).<br />
* Members which need to be accessed externally should be provided with get/set methods.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant_coding_standards&diff=2040DarkRadiant coding standards2006-10-28T09:34:39Z<p>OrbWeaver: Initial article</p>
<hr />
<div>Although DarkRadiant was forked from GTKRadiant, there are new '''coding standards''' which should be used for new code, in order to maximise readability for developers.<br />
<br />
==Naming conventions==<br />
<br />
* All new code should be in a namespace, such as '''ui''' for UI-related code or '''model''' for code dealing with models.<br />
* Class names should begin with a capital letter and capitalise each word: '''RenderablePicoModel''', '''TextMenuItem'''<br />
* Classes represent "things", therefore they should be named after nouns not verbs: '''ModuleLoader''' rather than '''LoadModule'''.<br />
* Method names and local variables should start with a small letter.<br />
* Member variables should begin with an underscore: '''_widget'''. Do not use the "m_name" convention in new code, as this is harder to read.<br />
<br />
==General code structure==<br />
<br />
* Always use Object-Oriented design methods wherever possible. Think in terms of objects which define methods to act on those objects, rather than C-style functions that process data structures passed as arguments. Non-member functions are OK for minor tasks, as long as they are within a namespace rather than at global scope.<br />
* Never use global variables.<br />
* If a static variable is required, encapsulate it within a small method that returns a reference to the static.</div>OrbWeaverhttps://wiki.thedarkmod.com/index.php?title=DarkRadiant&diff=2039DarkRadiant2006-10-28T09:25:17Z<p>OrbWeaver: </p>
<hr />
<div>DarkRadiant is The Dark Mod's very own map editor, based on the open-source GTKRadiant. <br />
<br />
Orbweaver started working on it in Spring 2006, the initial release was version 0.5.0. Features at this stage include a categorised entity inspector, drag-resizable lights, color selection tool for lights and numerous bugfixes over the GtkRadiant codebase.<br />
<br />
* [[DarkRadiant - Quick Installation Guide]]<br />
* The codebase is stored on sourceforge SVN: http://sourceforge.net/projects/darkradiant/<br />
<br />
==Developers==<br />
<br />
* [[DarkRadiant coding standards]]<br />
<br />
[[Category:DarkRadiant]]</div>OrbWeaver