From bdb412cd44fad1c8c9074a300f2f6dd47d32de32 Mon Sep 17 00:00:00 2001 From: cutealien Date: Sun, 17 Jun 2018 20:01:00 +0000 Subject: [PATCH] Merge revisions r5604 through r5621 from trunk to ogl-es. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@5622 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 16 + examples/09.Meshviewer/main.cpp | 8 +- include/EMeshWriterEnums.h | 3 + include/IGUISkin.h | 6 +- include/ITerrainSceneNode.h | 10 +- include/IVideoDriver.h | 24 +- include/S3DVertex.h | 8 +- include/SMaterial.h | 2 +- source/Irrlicht/CD3D9RenderTarget.cpp | 22 +- source/Irrlicht/CD3D9Texture.cpp | 8 +- source/Irrlicht/CGUIComboBox.cpp | 42 +- source/Irrlicht/CGUIComboBox.h | 1 + source/Irrlicht/CGUIListBox.cpp | 24 +- source/Irrlicht/CGUIListBox.h | 1 + source/Irrlicht/CGUIScrollBar.cpp | 22 +- source/Irrlicht/CGUITable.cpp | 31 +- source/Irrlicht/CGUITable.h | 5 +- source/Irrlicht/CGUITreeView.cpp | 43 +- source/Irrlicht/CGUITreeView.h | 4 + source/Irrlicht/CMeshManipulator.cpp | 268 ++-- source/Irrlicht/CNullDriver.cpp | 9 + source/Irrlicht/CNullDriver.h | 3 + source/Irrlicht/COpenGLCoreCacheHandler.h | 2 +- source/Irrlicht/CPLYMeshFileLoader.cpp | 5 +- source/Irrlicht/CPLYMeshWriter.cpp | 133 +- source/Irrlicht/CSTLMeshWriter.cpp | 2 +- source/Irrlicht/CTerrainSceneNode.cpp | 12 + source/Irrlicht/CTerrainSceneNode.h | 7 + tools/Exporters/Blender/B3DExport.py | 1611 +++++++++++++++++++++ 29 files changed, 2040 insertions(+), 292 deletions(-) create mode 100644 tools/Exporters/Blender/B3DExport.py diff --git a/changes.txt b/changes.txt index c76ca8d1..72ffc1ec 100644 --- a/changes.txt +++ b/changes.txt @@ -9,6 +9,22 @@ Changes in ogl-es (not yet released - will be merged with trunk at some point) -------------------------- Changes in 1.9 (not yet released) +- CGUIComboBox uses now EGDS_SCROLLBAR_SIZE instead of EGDS_WINDOW_BUTTON_WIDTH for the width of the listbox button to allow changing that without changing window topbar height. + Thanks @LunaRebirth for reporting. (Forum: http://irrlicht.sourceforge.net/forum/viewtopic.php?f=1&t=52297&p=303682#p303682) +- CGUIListbox, CGUITreeView and CGUITable now resize scrollbars when EGDS_SCROLLBAR_SIZE in the skin changes without having to re-create the elements. + This also fixes the problem that drawing looked wrong when this value got changed after the elements were created. + Thanks @LunaRebirth for reporting. (Forum: http://irrlicht.sourceforge.net/forum/viewtopic.php?f=1&t=52297&p=303682#p303682) +- Scrollbar buttons can no longer get larger than half the ScrollBar element to avoid them overlapping. +- Add IVideoDriver::swapMaterialRenderers to allow swapping the renderer used to render a certain material. +- IMeshManipulator functions createMeshWith1TCoords, createMeshWith2TCoords and createMeshWithTangents no longer weld vertices while converting meshes. Use IMeshManipulator::createMeshWelded if you need that welding. +- Add ITerrainSceneNode::setFixedBorderLOD to handle connecting terrain nodes without gaps. Thanks @diho for the bugreport, testcase and a patch proposal (http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=51220). +- PLY loader now works with files which use "st" instead of "uv" for texture coordinates (like generated from Blender or Assimp). Thanks @JLouisB for patch (http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=52261). +- STL writer does now also write binary files when EMWF_WRITE_BINARY flag is used. Based on patch from JLouisB (http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=52261). + (EMWF_WRITE_COMPRESSED also still works for downward compatibility) +- Improved PLY exporter. Thanks for Patch from JLouisB. (Forum: http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=52261): + - Support for UV and vertex colors. + - Support for binary PLY files export with the EMWF_WRITE_BINARY flag + - Fix for the meshes with 32 bits index - Fix wrong colors on big endian platforms with burnings renders. Thx @kas1e for reporting and @curaga for the patch (#318). Forum bug discussion at http://irrlicht.sourceforge.net/forum/viewtopic.php?f=7&t=52177. - Fix bug #440 where OpenGL driver enabled second texture for single-texture materials when setMaterial was called twice. Thx@ "number Zero" for bugreport and test-case. - Irrlicht icon now loaded with LR_DEFAULTSIZE to better support larger icon requests. Thx@ luthyr for report and bugfix. diff --git a/examples/09.Meshviewer/main.cpp b/examples/09.Meshviewer/main.cpp index 0998f49d..010ad6eb 100644 --- a/examples/09.Meshviewer/main.cpp +++ b/examples/09.Meshviewer/main.cpp @@ -29,7 +29,7 @@ using namespace gui; Some global variables used later on */ IrrlichtDevice *Device = 0; -core::stringc StartUpModelFile; +io::path StartUpModelFile; core::stringw MessageText; core::stringw Caption; scene::ISceneNode* Model = 0; @@ -166,10 +166,8 @@ Function loadModel() loads a model and displays it using an addAnimatedMeshSceneNode and the scene manager. Nothing difficult. It also displays a short message box, if the model could not be loaded. */ -void loadModel(const c8* fn) +void loadModel(const io::path& filename) { - io::path filename(fn); - io::path extension; core::getFileNameExtension(extension, filename); extension.make_lower(); @@ -440,7 +438,7 @@ public: // load the model file, selected in the file open dialog IGUIFileOpenDialog* dialog = (IGUIFileOpenDialog*)event.GUIEvent.Caller; - loadModel(core::stringc(dialog->getFileName()).c_str()); + loadModel(dialog->getFileNameP()); } break; diff --git a/include/EMeshWriterEnums.h b/include/EMeshWriterEnums.h index 11f2e299..16e1e376 100644 --- a/include/EMeshWriterEnums.h +++ b/include/EMeshWriterEnums.h @@ -45,9 +45,12 @@ namespace scene EMWF_NONE = 0, //! write lightmap textures out if possible + //! Currently not used by any Irrlicht mesh-writer + // (Note: User meshwriters can still use it) EMWF_WRITE_LIGHTMAPS = 0x1, //! write in a way that consumes less disk space + // (Note: Mainly there for user meshwriters) EMWF_WRITE_COMPRESSED = 0x2, //! write in binary format rather than text diff --git a/include/IGUISkin.h b/include/IGUISkin.h index d7cadcd4..e685a1ed 100644 --- a/include/IGUISkin.h +++ b/include/IGUISkin.h @@ -149,11 +149,11 @@ namespace gui //! Enumeration for default sizes. enum EGUI_DEFAULT_SIZE { - //! default with / height of scrollbar + //! default with / height of scrollbar. Also width of drop-down button in comboboxes. EGDS_SCROLLBAR_SIZE = 0, //! height of menu EGDS_MENU_HEIGHT, - //! width of a window button + //! width and height of a window titlebar button (like minimize/maximize/close buttons). The titlebar height is also calculated from that. EGDS_WINDOW_BUTTON_WIDTH, //! width of a checkbox check EGDS_CHECK_BOX_WIDTH, @@ -163,7 +163,7 @@ namespace gui EGDS_MESSAGE_BOX_HEIGHT, //! width of a default button EGDS_BUTTON_WIDTH, - //! height of a default button + //! height of a default button (OK and cancel buttons) EGDS_BUTTON_HEIGHT, //! distance for text from background EGDS_TEXT_DISTANCE_X, diff --git a/include/ITerrainSceneNode.h b/include/ITerrainSceneNode.h index f367792f..b0c822ce 100644 --- a/include/ITerrainSceneNode.h +++ b/include/ITerrainSceneNode.h @@ -100,7 +100,9 @@ namespace scene virtual s32 getCurrentLODOfPatches(core::array& LODs) const =0; //! Manually sets the LOD of a patch - /** \param patchX Patch x coordinate. + /** NOTE: Any values set here are overwritten again in the automatic + recalculations when the camera changes. + \param patchX Patch x coordinate. \param patchZ Patch z coordinate. \param LOD The level of detail to set the patch to. */ virtual void setLODOfPatch(s32 patchX, s32 patchZ, s32 LOD=0) =0; @@ -172,6 +174,12 @@ namespace scene video::SColor vertexColor=video::SColor(255,255,255,255), s32 smoothFactor=0) =0; + //! Force node to use a fixed LOD level at the borders of the terrain. + /** This can be useful when several TerrainSceneNodes are connected. + \param borderLOD When >= 0 all patches at the 4 borders will use the + given LOD. When < 0 borders are just regular patches (that's default). */ + virtual void setFixedBorderLOD(irr::s32 borderLOD=0) = 0; + }; } // end namespace scene diff --git a/include/IVideoDriver.h b/include/IVideoDriver.h index 4f38b26f..cabb1248 100644 --- a/include/IVideoDriver.h +++ b/include/IVideoDriver.h @@ -501,11 +501,15 @@ namespace video bool zeroTexels = false) const =0; //! Creates a normal map from a height map texture. - /** If the target texture has 32 bit, the height value is - stored in the alpha component of the texture as addition. This - value is used by the video::EMT_PARALLAX_MAP_SOLID material and - similar materials. - \param texture Texture whose alpha channel is modified. + /** As input is considered to be a height map the texture is read like: + - For a 32-bit texture only the red channel is regarded + - For a 16-bit texture the rgb-values are averaged. + Output channels red/green for X/Y and blue for up (Z). + For a 32-bit texture we store additionally the height value in the + alpha channel. This value is used by the video::EMT_PARALLAX_MAP_SOLID + material and similar materials. + On the borders the texture is considered to repeat. + \param texture Height map texture which is converted to a normal map. \param amplitude Constant value by which the height information is multiplied.*/ virtual void makeNormalMapTexture(video::ITexture* texture, f32 amplitude=1.0f) const =0; @@ -1327,6 +1331,16 @@ namespace video \param name: New name of the material renderer. */ virtual void setMaterialRendererName(s32 idx, const c8* name) =0; + //! Swap the material renderers used for certain id's + /** Swap the IMaterialRenderers responsible for rendering specific + material-id's. This means every SMaterial using a MaterialType + with one of the indices involved here will now render differently. + \param idx1 First material index to swap. It must already exist or nothing happens. + \param idx2 Second material index to swap. It must already exist or nothing happens. + \param swapNames When true the renderer names also swap + When false the names will stay at the original index */ + virtual void swapMaterialRenderers(u32 idx1, u32 idx2, bool swapNames=true) = 0; + //! Creates material attributes list from a material /** This method is useful for serialization and more. Please note that the video driver will use the material diff --git a/include/S3DVertex.h b/include/S3DVertex.h index bf0fd5b8..0950a4d4 100644 --- a/include/S3DVertex.h +++ b/include/S3DVertex.h @@ -25,7 +25,9 @@ enum E_VERTEX_TYPE EVT_2TCOORDS, //! Vertex with a tangent and binormal vector, video::S3DVertexTangents. - /** Usually used for tangent space normal mapping. */ + /** Usually used for tangent space normal mapping. + Usually tangent and binormal get send to shaders as texture coordinate sets 1 and 2. + */ EVT_TANGENTS }; @@ -181,7 +183,9 @@ struct S3DVertex2TCoords : public S3DVertex //! Vertex with a tangent and binormal vector. -/** Usually used for tangent space normal mapping. */ +/** Usually used for tangent space normal mapping. + Usually tangent and binormal get send to shaders as texture coordinate sets 1 and 2. +*/ struct S3DVertexTangents : public S3DVertex { //! default constructor diff --git a/include/SMaterial.h b/include/SMaterial.h index 2ef0457d..e259d765 100644 --- a/include/SMaterial.h +++ b/include/SMaterial.h @@ -440,7 +440,7 @@ namespace video f32 BlendFactor; //! Factor specifying how far the polygon offset should be made - /** Specifying 0 disables the polygon offset. The direction is specified spearately. + /** Specifying 0 disables the polygon offset. The direction is specified separately. The factor can be from 0 to 7.*/ u8 PolygonOffsetFactor:3; diff --git a/source/Irrlicht/CD3D9RenderTarget.cpp b/source/Irrlicht/CD3D9RenderTarget.cpp index d90a92f1..6e42e658 100644 --- a/source/Irrlicht/CD3D9RenderTarget.cpp +++ b/source/Irrlicht/CD3D9RenderTarget.cpp @@ -236,22 +236,24 @@ namespace irr if (!Surface[i] && Texture[i]) { IDirect3DTexture9* currentTexture = static_cast(Texture[i])->getDX9Texture(); - - IDirect3DSurface9* currentSurface = 0; - currentTexture->GetSurfaceLevel(0, ¤tSurface); - - Surface[i] = currentSurface; + if ( currentTexture ) + { + IDirect3DSurface9* currentSurface = 0; + currentTexture->GetSurfaceLevel(0, ¤tSurface); + Surface[i] = currentSurface; + } } } if (!DepthStencilSurface && DepthStencil) { IDirect3DTexture9* currentTexture = static_cast(DepthStencil)->getDX9Texture(); - - IDirect3DSurface9* currentSurface = 0; - currentTexture->GetSurfaceLevel(0, ¤tSurface); - - DepthStencilSurface = currentSurface; + if ( currentTexture ) + { + IDirect3DSurface9* currentSurface = 0; + currentTexture->GetSurfaceLevel(0, ¤tSurface); + DepthStencilSurface = currentSurface; + } } } } diff --git a/source/Irrlicht/CD3D9Texture.cpp b/source/Irrlicht/CD3D9Texture.cpp index d494cfa1..82677be6 100644 --- a/source/Irrlicht/CD3D9Texture.cpp +++ b/source/Irrlicht/CD3D9Texture.cpp @@ -353,13 +353,13 @@ void CD3D9Texture::generateRenderTarget() if (FAILED(hr)) { if (D3DERR_INVALIDCALL == hr) - os::Printer::log("Could not create render target texture", "Invalid Call"); + os::Printer::log("Could not create render target texture", "Invalid Call", irr::ELL_ERROR); else if (D3DERR_OUTOFVIDEOMEMORY == hr) - os::Printer::log("Could not create render target texture", "Out of Video Memory"); + os::Printer::log("Could not create render target texture", "Out of Video Memory", irr::ELL_ERROR); else if (E_OUTOFMEMORY == hr) - os::Printer::log("Could not create render target texture", "Out of Memory"); + os::Printer::log("Could not create render target texture", "Out of Memory", irr::ELL_ERROR); else - os::Printer::log("Could not create render target texture"); + os::Printer::log("Could not create render target texture", irr::ELL_ERROR); } } } diff --git a/source/Irrlicht/CGUIComboBox.cpp b/source/Irrlicht/CGUIComboBox.cpp index e26a9fec..1efd345b 100644 --- a/source/Irrlicht/CGUIComboBox.cpp +++ b/source/Irrlicht/CGUIComboBox.cpp @@ -33,18 +33,7 @@ CGUIComboBox::CGUIComboBox(IGUIEnvironment* environment, IGUIElement* parent, IGUISkin* skin = Environment->getSkin(); - s32 width = 15; - if (skin) - width = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH); - - core::rect r; - r.UpperLeftCorner.X = rectangle.getWidth() - width - 2; - r.LowerRightCorner.X = rectangle.getWidth() - 2; - - r.UpperLeftCorner.Y = 2; - r.LowerRightCorner.Y = rectangle.getHeight() - 2; - - ListButton = Environment->addButton(r, this, -1, L""); + ListButton = Environment->addButton(core::recti(0,0,1,1), this, -1, L""); if (skin && skin->getSpriteBank()) { ListButton->setSpriteBank(skin->getSpriteBank()); @@ -55,12 +44,7 @@ CGUIComboBox::CGUIComboBox(IGUIEnvironment* environment, IGUIElement* parent, ListButton->setSubElement(true); ListButton->setTabStop(false); - r.UpperLeftCorner.X = 2; - r.UpperLeftCorner.Y = 2; - r.LowerRightCorner.X = RelativeRect.getWidth() - (ListButton->getAbsolutePosition().getWidth() + 2); - r.LowerRightCorner.Y = RelativeRect.getHeight() - 2; - - SelectedText = Environment->addStaticText(L"", r, false, false, this, -1, false); + SelectedText = Environment->addStaticText(L"", core::recti(0,0,1,1), false, false, this, -1, false); SelectedText->setSubElement(true); SelectedText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT); SelectedText->setTextAlignment(EGUIA_UPPERLEFT, EGUIA_CENTER); @@ -68,6 +52,8 @@ CGUIComboBox::CGUIComboBox(IGUIEnvironment* environment, IGUIElement* parent, SelectedText->setOverrideColor(skin->getColor(EGDC_BUTTON_TEXT)); SelectedText->enableOverrideColor(true); + updateListButtonWidth(skin ? skin->getSize(EGDS_SCROLLBAR_SIZE) : 15); + // this element can be tabbed to setTabStop(true); setTabOrder(-1); @@ -377,6 +363,24 @@ void CGUIComboBox::sendSelectionChangedEvent() } } +void CGUIComboBox::updateListButtonWidth(s32 width) +{ + if (ListButton->getRelativePosition().getWidth() != width) + { + core::rect r; + r.UpperLeftCorner.X = RelativeRect.getWidth() - width - 2; + r.LowerRightCorner.X = RelativeRect.getWidth() - 2; + r.UpperLeftCorner.Y = 2; + r.LowerRightCorner.Y = RelativeRect.getHeight() - 2; + ListButton->setRelativePosition(r); + + r.UpperLeftCorner.X = 2; + r.UpperLeftCorner.Y = 2; + r.LowerRightCorner.X = RelativeRect.getWidth() - (width + 2); + r.LowerRightCorner.Y = RelativeRect.getHeight() - 2; + SelectedText->setRelativePosition(r); + } +} //! draws the element and its children void CGUIComboBox::draw() @@ -386,6 +390,8 @@ void CGUIComboBox::draw() IGUISkin* skin = Environment->getSkin(); + updateListButtonWidth(skin->getSize(EGDS_SCROLLBAR_SIZE)); + // font changed while the listbox is open? if ( ActiveFont != skin->getFont() && ListBox ) { diff --git a/source/Irrlicht/CGUIComboBox.h b/source/Irrlicht/CGUIComboBox.h index 1edcfb7b..5aa5de6d 100644 --- a/source/Irrlicht/CGUIComboBox.h +++ b/source/Irrlicht/CGUIComboBox.h @@ -84,6 +84,7 @@ namespace gui void openCloseMenu(); void sendSelectionChangedEvent(); + void updateListButtonWidth(s32 width); IGUIButton* ListButton; IGUIStaticText* SelectedText; diff --git a/source/Irrlicht/CGUIListBox.cpp b/source/Irrlicht/CGUIListBox.cpp index 58acbf5c..cffaa532 100644 --- a/source/Irrlicht/CGUIListBox.cpp +++ b/source/Irrlicht/CGUIListBox.cpp @@ -34,10 +34,9 @@ CGUIListBox::CGUIListBox(IGUIEnvironment* environment, IGUIElement* parent, #endif IGUISkin* skin = Environment->getSkin(); - const s32 s = skin->getSize(EGDS_SCROLLBAR_SIZE); - ScrollBar = new CGUIScrollBar(false, Environment, this, -1, - core::rect(RelativeRect.getWidth() - s, 0, RelativeRect.getWidth(), RelativeRect.getHeight()), + ScrollBar = new CGUIScrollBar(false, Environment, this, -1, + core::recti(0, 0, 1, 1), !clip); ScrollBar->setSubElement(true); ScrollBar->setTabStop(false); @@ -45,6 +44,8 @@ CGUIListBox::CGUIListBox(IGUIEnvironment* environment, IGUIElement* parent, ScrollBar->setVisible(false); ScrollBar->setPos(0); + updateScrollBarSize(skin->getSize(EGDS_SCROLLBAR_SIZE)); + setNotClipped(!clip); // this element can be tabbed to @@ -149,8 +150,7 @@ void CGUIListBox::clear() ItemsIconWidth = 0; Selected = -1; - if (ScrollBar) - ScrollBar->setPos(0); + ScrollBar->setPos(0); recalculateItemHeight(); } @@ -190,7 +190,6 @@ void CGUIListBox::recalculateItemHeight() ScrollBar->setVisible(true); } - //! returns id of selected item. returns -1 if no item is selected. s32 CGUIListBox::getSelected() const { @@ -495,6 +494,7 @@ void CGUIListBox::draw() recalculateItemHeight(); // if the font changed IGUISkin* skin = Environment->getSkin(); + updateScrollBarSize(skin->getSize(EGDS_SCROLLBAR_SIZE)); core::rect* clipRect = 0; @@ -507,7 +507,7 @@ void CGUIListBox::draw() clientClip.UpperLeftCorner.Y += 1; clientClip.UpperLeftCorner.X += 1; if (ScrollBar->isVisible()) - clientClip.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE); + clientClip.LowerRightCorner.X -= ScrollBar->getRelativePosition().getWidth(); clientClip.LowerRightCorner.Y -= 1; clientClip.clipAgainst(AbsoluteClippingRect); @@ -520,7 +520,7 @@ void CGUIListBox::draw() frameRect = AbsoluteRect; frameRect.UpperLeftCorner.X += 1; if (ScrollBar->isVisible()) - frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE); + frameRect.LowerRightCorner.X -= ScrollBar->getRelativePosition().getWidth(); frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight; @@ -637,6 +637,14 @@ void CGUIListBox::recalculateScrollPos() } } +void CGUIListBox::updateScrollBarSize(s32 size) +{ + if ( size != ScrollBar->getRelativePosition().getWidth() ) + { + core::recti r(RelativeRect.getWidth() - size, 0, RelativeRect.getWidth(), RelativeRect.getHeight()); + ScrollBar->setRelativePosition(r); + } +} void CGUIListBox::setAutoScrollEnabled(bool scroll) { diff --git a/source/Irrlicht/CGUIListBox.h b/source/Irrlicht/CGUIListBox.h index a94b7932..4fc61220 100644 --- a/source/Irrlicht/CGUIListBox.h +++ b/source/Irrlicht/CGUIListBox.h @@ -157,6 +157,7 @@ namespace gui void recalculateItemHeight(); void selectNew(s32 ypos, bool onlyHover=false); void recalculateScrollPos(); + void updateScrollBarSize(s32 size); // extracted that function to avoid copy&paste code void recalculateItemWidth(s32 icon); diff --git a/source/Irrlicht/CGUIScrollBar.cpp b/source/Irrlicht/CGUIScrollBar.cpp index e0a8a030..4fed3706 100644 --- a/source/Irrlicht/CGUIScrollBar.cpp +++ b/source/Irrlicht/CGUIScrollBar.cpp @@ -466,10 +466,11 @@ void CGUIScrollBar::refreshControls() if (Horizontal) { - s32 h = RelativeRect.getHeight(); + const s32 h = RelativeRect.getHeight(); + const s32 w = (h < RelativeRect.getWidth() / 2) ? h : RelativeRect.getWidth() / 2; if (!UpButton) { - UpButton = new CGUIButton(Environment, this, -1, core::rect(0,0, h, h), NoClip); + UpButton = new CGUIButton(Environment, this, -1, core::rect(0,0, w, h), NoClip); UpButton->setSubElement(true); UpButton->setTabStop(false); } @@ -479,11 +480,11 @@ void CGUIScrollBar::refreshControls() UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor); UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_LEFT), CurrentIconColor); } - UpButton->setRelativePosition(core::rect(0,0, h, h)); + UpButton->setRelativePosition(core::rect(0,0, w, h)); UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT); if (!DownButton) { - DownButton = new CGUIButton(Environment, this, -1, core::rect(RelativeRect.getWidth()-h, 0, RelativeRect.getWidth(), h), NoClip); + DownButton = new CGUIButton(Environment, this, -1, core::rect(RelativeRect.getWidth()-w, 0, RelativeRect.getWidth(), h), NoClip); DownButton->setSubElement(true); DownButton->setTabStop(false); } @@ -493,15 +494,16 @@ void CGUIScrollBar::refreshControls() DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor); DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_RIGHT), CurrentIconColor); } - DownButton->setRelativePosition(core::rect(RelativeRect.getWidth()-h, 0, RelativeRect.getWidth(), h)); + DownButton->setRelativePosition(core::rect(RelativeRect.getWidth()-w, 0, RelativeRect.getWidth(), h)); DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT); } else { - s32 w = RelativeRect.getWidth(); + const s32 w = RelativeRect.getWidth(); + const s32 h = (w < RelativeRect.getHeight() / 2) ? w : RelativeRect.getHeight() / 2; if (!UpButton) { - UpButton = new CGUIButton(Environment, this, -1, core::rect(0,0, w, w), NoClip); + UpButton = new CGUIButton(Environment, this, -1, core::rect(0,0, w, h), NoClip); UpButton->setSubElement(true); UpButton->setTabStop(false); } @@ -511,11 +513,11 @@ void CGUIScrollBar::refreshControls() UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor); UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_UP), CurrentIconColor); } - UpButton->setRelativePosition(core::rect(0,0, w, w)); + UpButton->setRelativePosition(core::rect(0,0, w, h)); UpButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT); if (!DownButton) { - DownButton = new CGUIButton(Environment, this, -1, core::rect(0,RelativeRect.getHeight()-w, w, RelativeRect.getHeight()), NoClip); + DownButton = new CGUIButton(Environment, this, -1, core::rect(0,RelativeRect.getHeight()-h, w, RelativeRect.getHeight()), NoClip); DownButton->setSubElement(true); DownButton->setTabStop(false); } @@ -525,7 +527,7 @@ void CGUIScrollBar::refreshControls() DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor); DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), CurrentIconColor); } - DownButton->setRelativePosition(core::rect(0,RelativeRect.getHeight()-w, w, RelativeRect.getHeight())); + DownButton->setRelativePosition(core::rect(0,RelativeRect.getHeight()-h, w, RelativeRect.getHeight())); DownButton->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT); } } diff --git a/source/Irrlicht/CGUITable.cpp b/source/Irrlicht/CGUITable.cpp index fac733b6..a0f1ced8 100644 --- a/source/Irrlicht/CGUITable.cpp +++ b/source/Irrlicht/CGUITable.cpp @@ -33,6 +33,7 @@ CGUITable::CGUITable(IGUIEnvironment* environment, IGUIElement* parent, ItemHeight(0), TotalItemHeight(0), TotalItemWidth(0), Selected(-1), CellHeightPadding(2), CellWidthPadding(5), ActiveTab(-1), CurrentOrdering(EGOM_NONE), DrawFlags(EGTDF_ROWS | EGTDF_COLUMNS | EGTDF_ACTIVE_ROW ), + ScrollBarSize(0), OverrideFont(0) { #ifdef _DEBUG @@ -436,7 +437,8 @@ void CGUITable::checkScrollbars() if ( !HorizontalScrollBar || !VerticalScrollBar || !skin) return; - s32 scrollBarSize = skin->getSize(EGDS_SCROLLBAR_SIZE); + ScrollBarSize = skin->getSize(EGDS_SCROLLBAR_SIZE); + bool wasHorizontalScrollBarVisible = HorizontalScrollBar->isVisible(); bool wasVerticalScrollBarVisible = VerticalScrollBar->isVisible(); HorizontalScrollBar->setVisible(false); @@ -456,7 +458,7 @@ void CGUITable::checkScrollbars() // needs horizontal scroll be visible? if( TotalItemWidth > clientClip.getWidth() ) { - clientClip.LowerRightCorner.Y -= scrollBarSize; + clientClip.LowerRightCorner.Y -= ScrollBarSize; HorizontalScrollBar->setVisible(true); HorizontalScrollBar->setMax(core::max_(0,TotalItemWidth - clientClip.getWidth())); } @@ -464,7 +466,7 @@ void CGUITable::checkScrollbars() // needs vertical scroll be visible? if( TotalItemHeight > clientClip.getHeight() ) { - clientClip.LowerRightCorner.X -= scrollBarSize; + clientClip.LowerRightCorner.X -= ScrollBarSize; VerticalScrollBar->setVisible(true); VerticalScrollBar->setMax(core::max_(0,TotalItemHeight - clientClip.getHeight())); @@ -473,7 +475,7 @@ void CGUITable::checkScrollbars() { if( TotalItemWidth > clientClip.getWidth() ) { - clientClip.LowerRightCorner.Y -= scrollBarSize; + clientClip.LowerRightCorner.Y -= ScrollBarSize; HorizontalScrollBar->setVisible(true); HorizontalScrollBar->setMax(core::max_(0,TotalItemWidth - clientClip.getWidth())); } @@ -489,13 +491,13 @@ void CGUITable::checkScrollbars() if ( HorizontalScrollBar->isVisible() ) { VerticalScrollBar->setRelativePosition( - core::rect(RelativeRect.getWidth() - scrollBarSize, 1, - RelativeRect.getWidth()-1, RelativeRect.getHeight()-(1+scrollBarSize) ) ); + core::rect(RelativeRect.getWidth() - ScrollBarSize, 1, + RelativeRect.getWidth()-1, RelativeRect.getHeight()-(1+ScrollBarSize) ) ); } else { VerticalScrollBar->setRelativePosition( - core::rect(RelativeRect.getWidth() - scrollBarSize, 1, + core::rect(RelativeRect.getWidth() - ScrollBarSize, 1, RelativeRect.getWidth()-1, RelativeRect.getHeight()-1) ); } } @@ -508,11 +510,11 @@ void CGUITable::checkScrollbars() if ( VerticalScrollBar->isVisible() ) { - HorizontalScrollBar->setRelativePosition( core::rect(1, RelativeRect.getHeight() - scrollBarSize, RelativeRect.getWidth()-(1+scrollBarSize), RelativeRect.getHeight()-1) ); + HorizontalScrollBar->setRelativePosition( core::rect(1, RelativeRect.getHeight() - ScrollBarSize, RelativeRect.getWidth()-(1+ScrollBarSize), RelativeRect.getHeight()-1) ); } else { - HorizontalScrollBar->setRelativePosition( core::rect(1, RelativeRect.getHeight() - scrollBarSize, RelativeRect.getWidth()-1, RelativeRect.getHeight()-1) ); + HorizontalScrollBar->setRelativePosition( core::rect(1, RelativeRect.getHeight() - ScrollBarSize, RelativeRect.getWidth()-1, RelativeRect.getHeight()-1) ); } } } @@ -864,15 +866,18 @@ void CGUITable::draw() if (!font) return; + if ( ScrollBarSize != skin->getSize(EGDS_SCROLLBAR_SIZE) ) + checkScrollbars(); + // CAREFUL: near identical calculations for tableRect and clientClip are also done in checkScrollbars and selectColumnHeader // Area of table used for drawing without scrollbars core::rect tableRect(AbsoluteRect); tableRect.UpperLeftCorner.X += 1; tableRect.UpperLeftCorner.Y += 1; if ( VerticalScrollBar && VerticalScrollBar->isVisible() ) - tableRect.LowerRightCorner.X -= skin->getSize(EGDS_SCROLLBAR_SIZE); + tableRect.LowerRightCorner.X -= ScrollBarSize; if ( HorizontalScrollBar && HorizontalScrollBar->isVisible() ) - tableRect.LowerRightCorner.Y -= skin->getSize(EGDS_SCROLLBAR_SIZE); + tableRect.LowerRightCorner.Y -= ScrollBarSize; s32 headerBottom = tableRect.UpperLeftCorner.Y + ItemHeight; @@ -910,7 +915,7 @@ void CGUITable::draw() if (rowRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y && rowRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y) { - // draw row seperator + // draw row separator if ( DrawFlags & EGTDF_ROWS ) { core::rect lineRect(rowRect); @@ -966,7 +971,7 @@ void CGUITable::draw() // draw column background skin->draw3DButtonPaneStandard(this, columnrect, &tableClip); - // draw column seperator + // draw column separator if ( DrawFlags & EGTDF_COLUMNS ) { columnSeparator.UpperLeftCorner.X = pos; diff --git a/source/Irrlicht/CGUITable.h b/source/Irrlicht/CGUITable.h index 29e66133..f325f49f 100644 --- a/source/Irrlicht/CGUITable.h +++ b/source/Irrlicht/CGUITable.h @@ -179,8 +179,8 @@ namespace gui virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) _IRR_OVERRIDE_; protected: - virtual void refreshControls(); - virtual void checkScrollbars(); + void refreshControls(); + void checkScrollbars(); private: @@ -240,6 +240,7 @@ namespace gui s32 ActiveTab; EGUI_ORDERING_MODE CurrentOrdering; s32 DrawFlags; + s32 ScrollBarSize; gui::IGUIFont* OverrideFont; }; diff --git a/source/Irrlicht/CGUITreeView.cpp b/source/Irrlicht/CGUITreeView.cpp index 7a7accf5..f6d7743a 100644 --- a/source/Irrlicht/CGUITreeView.cpp +++ b/source/Irrlicht/CGUITreeView.cpp @@ -432,6 +432,7 @@ CGUITreeView::CGUITreeView(IGUIEnvironment* environment, IGUIElement* parent, IndentWidth( 0 ), TotalItemHeight( 0 ), TotalItemWidth ( 0 ), + ScrollBarSize( 0 ), Font( 0 ), OverrideFont( 0 ), IconFont( 0 ), @@ -450,15 +451,15 @@ CGUITreeView::CGUITreeView(IGUIEnvironment* environment, IGUIElement* parent, #endif IGUISkin* skin = Environment->getSkin(); - s32 s = skin->getSize( EGDS_SCROLLBAR_SIZE ); + ScrollBarSize = skin->getSize( EGDS_SCROLLBAR_SIZE ); if ( scrollBarVertical ) { ScrollBarV = new CGUIScrollBar( false, Environment, this, -1, - core::rect( RelativeRect.getWidth() - s, + core::rect( RelativeRect.getWidth() - ScrollBarSize, 0, RelativeRect.getWidth(), - RelativeRect.getHeight() - s + RelativeRect.getHeight() - ScrollBarSize ), !clip ); ScrollBarV->drop(); @@ -471,8 +472,8 @@ CGUITreeView::CGUITreeView(IGUIEnvironment* environment, IGUIElement* parent, { ScrollBarH = new CGUIScrollBar( true, Environment, this, -1, core::rect( 0, - RelativeRect.getHeight() - s, - RelativeRect.getWidth() - s, + RelativeRect.getHeight() - ScrollBarSize, + RelativeRect.getWidth() - ScrollBarSize, RelativeRect.getHeight() ), !clip ); ScrollBarH->drop(); @@ -640,6 +641,28 @@ void CGUITreeView::recalculateItemHeight() } +void CGUITreeView::updateScrollBarSize(s32 size) +{ + if ( size != ScrollBarSize ) + { + ScrollBarSize = size; + + if ( ScrollBarV ) + { + core::recti r(RelativeRect.getWidth() - ScrollBarSize, 0, + RelativeRect.getWidth(), RelativeRect.getHeight() - ScrollBarSize); + ScrollBarV->setRelativePosition(r); + } + + if ( ScrollBarH ) + { + core::recti r(0, RelativeRect.getHeight() - ScrollBarSize, + RelativeRect.getWidth() - ScrollBarSize, RelativeRect.getHeight()); + ScrollBarH->setRelativePosition(r); + } + } +} + //! called if an event happened. bool CGUITreeView::OnEvent( const SEvent &event ) { @@ -829,9 +852,11 @@ void CGUITreeView::draw() return; } + IGUISkin* skin = Environment->getSkin(); + + updateScrollBarSize(skin->getSize(EGDS_SCROLLBAR_SIZE)); recalculateItemHeight(); // if the font changed - IGUISkin* skin = Environment->getSkin(); irr::video::IVideoDriver* driver = Environment->getVideoDriver(); core::rect* clipRect = 0; @@ -876,9 +901,9 @@ void CGUITreeView::draw() clientClip.LowerRightCorner.Y -= 1; if ( ScrollBarV ) - clientClip.LowerRightCorner.X -= skin->getSize( EGDS_SCROLLBAR_SIZE ); + clientClip.LowerRightCorner.X -= ScrollBarSize; if ( ScrollBarH ) - clientClip.LowerRightCorner.Y -= skin->getSize( EGDS_SCROLLBAR_SIZE ); + clientClip.LowerRightCorner.Y -= ScrollBarSize; if( clipRect ) { @@ -886,7 +911,7 @@ void CGUITreeView::draw() } frameRect = AbsoluteRect; - frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize( EGDS_SCROLLBAR_SIZE ); + frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - ScrollBarSize; frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight; if ( ScrollBarV ) diff --git a/source/Irrlicht/CGUITreeView.h b/source/Irrlicht/CGUITreeView.h index 65cb21a1..bdcf035e 100644 --- a/source/Irrlicht/CGUITreeView.h +++ b/source/Irrlicht/CGUITreeView.h @@ -320,6 +320,9 @@ namespace gui //! calculates the heigth of an node and of all visible nodes. void recalculateItemHeight(); + //! Resize scrollbars when their size in the skin has changed + void updateScrollBarSize(s32 size); + //! executes an mouse action (like selectNew of CGUIListBox) void mouseAction( s32 xpos, s32 ypos, bool onlyHover = false ); @@ -329,6 +332,7 @@ namespace gui s32 IndentWidth; s32 TotalItemHeight; s32 TotalItemWidth; + s32 ScrollBarSize; IGUIFont* Font; gui::IGUIFont* OverrideFont; IGUIFont* IconFont; diff --git a/source/Irrlicht/CMeshManipulator.cpp b/source/Irrlicht/CMeshManipulator.cpp index e8532880..f177fc26 100644 --- a/source/Irrlicht/CMeshManipulator.cpp +++ b/source/Irrlicht/CMeshManipulator.cpp @@ -951,75 +951,63 @@ IMesh* CMeshManipulator::createMeshWelded(IMesh *mesh, f32 tolerance) const // not yet 32bit IMesh* CMeshManipulator::createMeshWithTangents(IMesh* mesh, bool recalculateNormals, bool smooth, bool angleWeighted, bool calculateTangents) const { + using namespace video; + if (!mesh) return 0; // copy mesh and fill data into SMeshBufferTangents - SMesh* clone = new SMesh(); const u32 meshBufferCount = mesh->getMeshBufferCount(); for (u32 b=0; bgetMeshBuffer(b); - const u32 idxCnt = original->getIndexCount(); - const u16* idx = original->getIndices(); - SMeshBufferTangents* buffer = new SMeshBufferTangents(); + // copy material buffer->Material = original->getMaterial(); - buffer->Vertices.reallocate(idxCnt); - buffer->Indices.reallocate(idxCnt); - core::map vertMap; - int vertLocation; + // copy indices + const u32 idxCnt = original->getIndexCount(); + const u16* indices = original->getIndices(); + buffer->Indices.reallocate(idxCnt); + for (u32 i=0; i < idxCnt; ++i) + buffer->Indices.push_back(indices[i]); // copy vertices + const u32 vtxCnt = original->getVertexCount(); + buffer->Vertices.reallocate(vtxCnt); - const video::E_VERTEX_TYPE vType = original->getVertexType(); - video::S3DVertexTangents vNew; - for (u32 i=0; igetVertexType(); + switch(vType) { - switch(vType) + case video::EVT_STANDARD: { - case video::EVT_STANDARD: - { - const video::S3DVertex* v = - (const video::S3DVertex*)original->getVertices(); - vNew = video::S3DVertexTangents( - v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords); - } - break; - case video::EVT_2TCOORDS: - { - const video::S3DVertex2TCoords* v = - (const video::S3DVertex2TCoords*)original->getVertices(); - vNew = video::S3DVertexTangents( - v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords); - } - break; - case video::EVT_TANGENTS: - { - const video::S3DVertexTangents* v = - (const video::S3DVertexTangents*)original->getVertices(); - vNew = v[idx[i]]; - } - break; - } - core::map::Node* n = vertMap.find(vNew); - if (n) - { - vertLocation = n->getValue(); - } - else - { - vertLocation = buffer->Vertices.size(); - buffer->Vertices.push_back(vNew); - vertMap.insert(vNew, vertLocation); - } + const S3DVertex* v = (const S3DVertex*)original->getVertices(); - // create new indices - buffer->Indices.push_back(vertLocation); + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( S3DVertexTangents( + v[i].Pos, v[i].Normal, v[i].Color, v[i].TCoords) ); + } + break; + case video::EVT_2TCOORDS: + { + const S3DVertex2TCoords* v =(const S3DVertex2TCoords*)original->getVertices(); + + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( S3DVertexTangents( + v[i].Pos, v[i].Normal, v[i].Color, v[i].TCoords) ); + } + break; + case video::EVT_TANGENTS: + { + const S3DVertexTangents* v =(const S3DVertexTangents*)original->getVertices(); + + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back(v[i]); + } + break; } buffer->recalculateBoundingBox(); @@ -1347,11 +1335,12 @@ donehere: free(accel); } - //! Creates a copy of the mesh, which will only consist of S3DVertex2TCoords vertices. // not yet 32bit IMesh* CMeshManipulator::createMeshWith2TCoords(IMesh* mesh) const { + using namespace video; + if (!mesh) return 0; @@ -1363,63 +1352,50 @@ IMesh* CMeshManipulator::createMeshWith2TCoords(IMesh* mesh) const for (u32 b=0; bgetMeshBuffer(b); - const u32 idxCnt = original->getIndexCount(); - const u16* idx = original->getIndices(); - SMeshBufferLightMap* buffer = new SMeshBufferLightMap(); - buffer->Material = original->getMaterial(); - buffer->Vertices.reallocate(idxCnt); - buffer->Indices.reallocate(idxCnt); - core::map vertMap; - int vertLocation; + // copy material + buffer->Material = original->getMaterial(); + + // copy indices + const u32 idxCnt = original->getIndexCount(); + const u16* indices = original->getIndices(); + buffer->Indices.reallocate(idxCnt); + for (u32 i=0; i < idxCnt; ++i) + buffer->Indices.push_back(indices[i]); // copy vertices + const u32 vtxCnt = original->getVertexCount(); + buffer->Vertices.reallocate(vtxCnt); const video::E_VERTEX_TYPE vType = original->getVertexType(); - video::S3DVertex2TCoords vNew; - for (u32 i=0; igetVertices(); - vNew = video::S3DVertex2TCoords( - v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords, v[idx[i]].TCoords); - } - break; - case video::EVT_2TCOORDS: - { - const video::S3DVertex2TCoords* v = - (const video::S3DVertex2TCoords*)original->getVertices(); - vNew = v[idx[i]]; - } - break; - case video::EVT_TANGENTS: - { - const video::S3DVertexTangents* v = - (const video::S3DVertexTangents*)original->getVertices(); - vNew = video::S3DVertex2TCoords( - v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords, v[idx[i]].TCoords); - } - break; - } - core::map::Node* n = vertMap.find(vNew); - if (n) - { - vertLocation = n->getValue(); - } - else - { - vertLocation = buffer->Vertices.size(); - buffer->Vertices.push_back(vNew); - vertMap.insert(vNew, vertLocation); - } + const S3DVertex* v = (const S3DVertex*)original->getVertices(); - // create new indices - buffer->Indices.push_back(vertLocation); + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( video::S3DVertex2TCoords( + v[i].Pos, v[i].Normal, v[i].Color, v[i].TCoords, v[i].TCoords)); + } + break; + case video::EVT_2TCOORDS: + { + const S3DVertex2TCoords* v =(const S3DVertex2TCoords*)original->getVertices(); + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back(v[i]); + } + break; + case video::EVT_TANGENTS: + { + const S3DVertexTangents* v =(const S3DVertexTangents*)original->getVertices(); + + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( S3DVertex2TCoords( + v[i].Pos, v[i].Normal, v[i].Color, v[i].TCoords, v[i].TCoords) ); + } + break; } buffer->recalculateBoundingBox(); @@ -1437,6 +1413,8 @@ IMesh* CMeshManipulator::createMeshWith2TCoords(IMesh* mesh) const // not yet 32bit IMesh* CMeshManipulator::createMeshWith1TCoords(IMesh* mesh) const { + using namespace video; + if (!mesh) return 0; @@ -1446,64 +1424,54 @@ IMesh* CMeshManipulator::createMeshWith1TCoords(IMesh* mesh) const for (u32 b=0; bgetMeshBuffer(b); - const u32 idxCnt = original->getIndexCount(); - const u16* idx = original->getIndices(); - + const IMeshBuffer* const original = mesh->getMeshBuffer(b); SMeshBuffer* buffer = new SMeshBuffer(); - buffer->Material = original->getMaterial(); - buffer->Vertices.reallocate(idxCnt); - buffer->Indices.reallocate(idxCnt); - core::map vertMap; - int vertLocation; + // copy material + buffer->Material = original->getMaterial(); + + // copy indices + const u32 idxCnt = original->getIndexCount(); + const u16* indices = original->getIndices(); + buffer->Indices.reallocate(idxCnt); + for (u32 i=0; i < idxCnt; ++i) + buffer->Indices.push_back(indices[i]); // copy vertices - const video::E_VERTEX_TYPE vType = original->getVertexType(); - video::S3DVertex vNew; - for (u32 i=0; igetVertices(); - vNew = v[idx[i]]; - } - break; - case video::EVT_2TCOORDS: - { - video::S3DVertex2TCoords* v = - (video::S3DVertex2TCoords*)original->getVertices(); - vNew = video::S3DVertex( - v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords); - } - break; - case video::EVT_TANGENTS: - { - video::S3DVertexTangents* v = - (video::S3DVertexTangents*)original->getVertices(); - vNew = video::S3DVertex( - v[idx[i]].Pos, v[idx[i]].Normal, v[idx[i]].Color, v[idx[i]].TCoords); - } - break; - } - core::map::Node* n = vertMap.find(vNew); - if (n) - { - vertLocation = n->getValue(); - } - else - { - vertLocation = buffer->Vertices.size(); - buffer->Vertices.push_back(vNew); - vertMap.insert(vNew, vertLocation); - } + const u32 vtxCnt = original->getVertexCount(); + buffer->Vertices.reallocate(vtxCnt); - // create new indices - buffer->Indices.push_back(vertLocation); + const video::E_VERTEX_TYPE vType = original->getVertexType(); + switch(vType) + { + case video::EVT_STANDARD: + { + const S3DVertex* v = (const S3DVertex*)original->getVertices(); + + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( v[i] ); + } + break; + case video::EVT_2TCOORDS: + { + const S3DVertex2TCoords* v =(const S3DVertex2TCoords*)original->getVertices(); + + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( S3DVertex( + v[i].Pos, v[i].Normal, v[i].Color, v[i].TCoords) ); + } + break; + case video::EVT_TANGENTS: + { + const S3DVertexTangents* v =(const S3DVertexTangents*)original->getVertices(); + + for (u32 i=0; i < vtxCnt; ++i) + buffer->Vertices.push_back( S3DVertex( + v[i].Pos, v[i].Normal, v[i].Color, v[i].TCoords) ); + } + break; } + buffer->recalculateBoundingBox(); // add new buffer clone->addMeshBuffer(buffer); diff --git a/source/Irrlicht/CNullDriver.cpp b/source/Irrlicht/CNullDriver.cpp index d76ff7c5..eabf6081 100644 --- a/source/Irrlicht/CNullDriver.cpp +++ b/source/Irrlicht/CNullDriver.cpp @@ -2087,6 +2087,15 @@ void CNullDriver::setMaterialRendererName(s32 idx, const char* name) MaterialRenderers[idx].Name = name; } +void CNullDriver::swapMaterialRenderers(u32 idx1, u32 idx2, bool swapNames) +{ + if ( idx1 < MaterialRenderers.size() && idx2 < MaterialRenderers.size() ) + { + irr::core::swap(MaterialRenderers[idx1].Renderer, MaterialRenderers[idx2].Renderer); + if ( swapNames ) + irr::core::swap(MaterialRenderers[idx1].Name, MaterialRenderers[idx2].Name); + } +} //! Creates material attributes list from a material, usable for serialization and more. io::IAttributes* CNullDriver::createAttributesFromMaterial(const video::SMaterial& material, diff --git a/source/Irrlicht/CNullDriver.h b/source/Irrlicht/CNullDriver.h index 725821e9..2172ae9f 100644 --- a/source/Irrlicht/CNullDriver.h +++ b/source/Irrlicht/CNullDriver.h @@ -612,6 +612,9 @@ namespace video //! Sets the name of a material renderer. virtual void setMaterialRendererName(s32 idx, const char* name) _IRR_OVERRIDE_; + //! Swap the material renderers used for certain id's + virtual void swapMaterialRenderers(u32 idx1, u32 idx2, bool swapNames) _IRR_OVERRIDE_; + //! Creates material attributes list from a material, usable for serialization and more. virtual io::IAttributes* createAttributesFromMaterial(const video::SMaterial& material, io::SAttributeReadWriteOptions* options=0) _IRR_OVERRIDE_; diff --git a/source/Irrlicht/COpenGLCoreCacheHandler.h b/source/Irrlicht/COpenGLCoreCacheHandler.h index 00ce69a9..7f2c451e 100644 --- a/source/Irrlicht/COpenGLCoreCacheHandler.h +++ b/source/Irrlicht/COpenGLCoreCacheHandler.h @@ -571,7 +571,7 @@ public: //! Compare material to current cache and update it when there are differences // Some material renderers do change the cache beyond the original material settings - // This correct the material to represent the current cache state again. + // This corrects the material to represent the current cache state again. void correctCacheMaterial(irr::video::SMaterial& material) { // Fix textures which got removed diff --git a/source/Irrlicht/CPLYMeshFileLoader.cpp b/source/Irrlicht/CPLYMeshFileLoader.cpp index 290e704a..03b3e471 100644 --- a/source/Irrlicht/CPLYMeshFileLoader.cpp +++ b/source/Irrlicht/CPLYMeshFileLoader.cpp @@ -324,9 +324,10 @@ bool CPLYMeshFileLoader::readVertex(const SPLYElement &Element, scene::CDynamicM vert.Normal.Y = getFloat(t); result=true; } - else if (Element.Properties[i].Name == "u") + // there isn't a single convention for the UV, some software like Blender or Assimp uses "st" instead of "uv" + else if (Element.Properties[i].Name == "u" || Element.Properties[i].Name == "s") vert.TCoords.X = getFloat(t); - else if (Element.Properties[i].Name == "v") + else if (Element.Properties[i].Name == "v" || Element.Properties[i].Name == "t") vert.TCoords.Y = getFloat(t); else if (Element.Properties[i].Name == "red") { diff --git a/source/Irrlicht/CPLYMeshWriter.cpp b/source/Irrlicht/CPLYMeshWriter.cpp index 9be0cdc2..8713c582 100644 --- a/source/Irrlicht/CPLYMeshWriter.cpp +++ b/source/Irrlicht/CPLYMeshWriter.cpp @@ -39,11 +39,21 @@ bool CPLYMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla os::Printer::log("Writing mesh", file->getFileName()); - // write PLY header - core::stringc header = - "ply\n" - "format ascii 1.0\n" - "comment Irrlicht Engine "; + // write PLY header + core::stringc header = "ply\n"; + + if (flags & scene::EMWF_WRITE_BINARY) + { + #ifdef __BIG_ENDIAN__ + header += "format binary_big_endian 1.0\n"; + #else + header += "format binary_little_endian 1.0\n"; + #endif + } + else + header += "format ascii 1.0\n"; + + header += "comment Irrlicht Engine "; header += IRRLICHT_SDK_VERSION; // get vertex and triangle counts @@ -66,16 +76,12 @@ bool CPLYMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla "property float z\n" "property float nx\n" "property float ny\n" - "property float nz\n"; - // todo: writer flags for extended (r,g,b,u,v) and non-standard (alpha,u1,uv,tx,ty,tz) properties - // "property uchar red\n" - // "property uchar green\n" - // "property uchar blue\n" - // "property uchar alpha\n" - // "property float u\n" - // "property float v\n"; - // "property float u1\n - // "property float v1\n" + "property float nz\n" + "property float s\n" + "property float t\n" + "property uchar red\n" + "property uchar green\n" + "property uchar blue\n"; // "property float tx\n" // "property float ty\n" // "property float tz\n" @@ -97,38 +103,61 @@ bool CPLYMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla for (u32 i=0; i < mesh->getMeshBufferCount(); ++i) { - scene::IMeshBuffer* mb = mesh->getMeshBuffer(i); + const scene::IMeshBuffer* mb = mesh->getMeshBuffer(i); + u32 vertexSize = 0; + switch(mb->getVertexType()) + { + case video::EVT_STANDARD: + vertexSize = sizeof(video::S3DVertex); + break; + case video::EVT_2TCOORDS: + vertexSize = sizeof(video::S3DVertex2TCoords); + break; + case video::EVT_TANGENTS: + vertexSize = sizeof(video::S3DVertexTangents); + break; + } + u8 *vertices = (u8*)mb->getVertices() ; + for (u32 j=0; j < mb->getVertexCount(); ++j) { - const core::vector3df& pos = mb->getPosition(j); - const core::vector3df& n = mb->getNormal(j); -// const core::vector2df& tc = mb->getTCoords(j); + u8 *buf = vertices + j * vertexSize; + const video::S3DVertex* vertex = ( (video::S3DVertex*)buf ); + const core::vector3df& pos = vertex->Pos; + const core::vector3df& n = vertex->Normal; + const core::vector2df& uv = vertex->TCoords; + const video::SColor& color = vertex->Color; - u8 *buf = (u8*)mb->getVertices(); - switch(mb->getVertexType()) + if (flags & scene::EMWF_WRITE_BINARY) { - case video::EVT_STANDARD: - buf += sizeof(video::S3DVertex)*j; - break; - case video::EVT_2TCOORDS: - buf += sizeof(video::S3DVertex2TCoords)*j; - break; - case video::EVT_TANGENTS: - buf += sizeof(video::S3DVertexTangents)*j; - break; + // Y and Z are flipped + file->write(&pos.X, 4); + file->write(&pos.Z, 4); + file->write(&pos.Y, 4); + + file->write(&n.X, 4); + file->write(&n.Z, 4); + file->write(&n.Y, 4); + + file->write(&uv, 8); + + const u32 r = color.getRed(), g = color.getGreen(), b = color.getBlue(); + file->write(&r, 1); + file->write(&g, 1); + file->write(&b, 1); } -// video::SColor &col = ( (video::S3DVertex*)buf )->Color; + else + { + // x y z nx ny nz u v red green blue [u1 v1 | tx ty tz]\n + snprintf_irr(outLine, 1024, + "%f %f %f %f %f %f %f %f %d %d %d\n",// %u %u %u %u %f %f\n", + pos.X, pos.Z, pos.Y, // Y and Z are flipped + n.X, n.Z, n.Y, + uv.X, uv.Y, + color.getRed(), color.getGreen(), color.getBlue()); - // x y z nx ny nz red green blue alpha u v [u1 v1 | tx ty tz]\n - snprintf_irr(outLine, 1024, - "%f %f %f %f %f %f\n",// %u %u %u %u %f %f\n", - pos.X, pos.Z, pos.Y, // Y and Z are flipped - n.X, n.Z, n.Y); - /*col.getRed(), col.getGreen(), col.getBlue(), col.getAlpha(), - tc.X, tc.Y);*/ - - // write the line - file->write(outLine, strlen(outLine)); + file->write(outLine, strlen(outLine)); + } } } @@ -136,6 +165,7 @@ bool CPLYMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla u32 StartOffset = 0; // write triangles + const unsigned char nbIndicesParFace = 3; for (u32 i=0; i < mesh->getMeshBufferCount(); ++i) { scene::IMeshBuffer* mb = mesh->getMeshBuffer(i); @@ -155,15 +185,24 @@ bool CPLYMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla break; case video::EIT_32BIT: a += ((u32*)mb->getIndices()) [j+0]; - c += ((u32*)mb->getIndices()) [j+0]; - b += ((u32*)mb->getIndices()) [j+0]; + c += ((u32*)mb->getIndices()) [j+1]; + b += ((u32*)mb->getIndices()) [j+2]; break; } - // count a b c\n - snprintf_irr(outLine, 1024, "3 %u %u %u\n", a, b, c); - // write the line - file->write(outLine, strlen(outLine)); + if (flags & scene::EMWF_WRITE_BINARY) + { + file->write(&nbIndicesParFace, 1); + file->write(&a, 4); + file->write(&b, 4); + file->write(&c, 4); + } + else + { + // count a b c\n + snprintf_irr(outLine, 1024, "3 %u %u %u\n", a, b, c); + file->write(outLine, strlen(outLine)); + } } // increment offset diff --git a/source/Irrlicht/CSTLMeshWriter.cpp b/source/Irrlicht/CSTLMeshWriter.cpp index 998a73f7..f3b14133 100644 --- a/source/Irrlicht/CSTLMeshWriter.cpp +++ b/source/Irrlicht/CSTLMeshWriter.cpp @@ -54,7 +54,7 @@ bool CSTLMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 fla os::Printer::log("Writing mesh", file->getFileName()); - if (flags & scene::EMWF_WRITE_COMPRESSED) + if (flags & (scene::EMWF_WRITE_BINARY|scene::EMWF_WRITE_COMPRESSED) ) return writeMeshBinary(file, mesh, flags); else return writeMeshASCII(file, mesh, flags); diff --git a/source/Irrlicht/CTerrainSceneNode.cpp b/source/Irrlicht/CTerrainSceneNode.cpp index e6981b06..91a060e2 100644 --- a/source/Irrlicht/CTerrainSceneNode.cpp +++ b/source/Irrlicht/CTerrainSceneNode.cpp @@ -38,6 +38,7 @@ namespace scene TerrainData(patchSize, maxLOD, position, rotation, scale), RenderBuffer(0), VerticesToRender(0), IndicesToRender(0), DynamicSelectorUpdate(false), OverrideDistanceThreshold(false), UseDefaultRotationPivot(true), ForceRecalculation(true), + FixedBorderLOD(-1), CameraMovementDelta(10.0f), CameraRotationDelta(1.0f),CameraFOVDelta(0.1f), TCoordScale1(1.0f), TCoordScale2(1.0f), SmoothFactor(0), FileSystem(fs) { @@ -640,7 +641,18 @@ namespace scene { const f32 distance = cameraPosition.getDistanceFromSQ(TerrainData.Patches[j].Center); + if ( FixedBorderLOD >= 0 ) + { + TerrainData.Patches[j].CurrentLOD = FixedBorderLOD; + if (j < TerrainData.PatchCount + || j >= (count - TerrainData.PatchCount) + || (j % TerrainData.PatchCount) == 0 + || (j % TerrainData.PatchCount) == TerrainData.PatchCount-1) + continue; + } + TerrainData.Patches[j].CurrentLOD = 0; + for (s32 i = TerrainData.MaxLOD - 1; i>0; --i) { if (distance >= TerrainData.LODDistanceThreshold[i]) diff --git a/source/Irrlicht/CTerrainSceneNode.h b/source/Irrlicht/CTerrainSceneNode.h index a1069a6a..3e22d69a 100644 --- a/source/Irrlicht/CTerrainSceneNode.h +++ b/source/Irrlicht/CTerrainSceneNode.h @@ -202,6 +202,12 @@ namespace scene //! Scales the two textures virtual void scaleTexture(f32 scale = 1.0f, f32 scale2 = 0.0f) _IRR_OVERRIDE_; + //! Force node to use a fixed LOD level at the borders of the terrain. + virtual void setFixedBorderLOD(irr::s32 borderLOD) _IRR_OVERRIDE_ + { + FixedBorderLOD = borderLOD; + } + //! Returns type of the scene node virtual ESCENE_NODE_TYPE getType() const _IRR_OVERRIDE_ {return ESNT_TERRAIN;} @@ -303,6 +309,7 @@ namespace scene bool OverrideDistanceThreshold; bool UseDefaultRotationPivot; bool ForceRecalculation; + s32 FixedBorderLOD; core::vector3df OldCameraPosition; core::vector3df OldCameraRotation; diff --git a/tools/Exporters/Blender/B3DExport.py b/tools/Exporters/Blender/B3DExport.py new file mode 100644 index 00000000..025b64eb --- /dev/null +++ b/tools/Exporters/Blender/B3DExport.py @@ -0,0 +1,1611 @@ +#!BPY + +""" +Name: 'B3D Exporter (.b3d)...' +Blender: 259 +Group: 'Export' +Tooltip: 'Export to Blitz3D file format (.b3d)' +""" +__author__ = ["iego 'GaNDaLDF' Parisi, MTLZ (is06), Joerg Henrichs, Marianne Gagnon"] +__url__ = ["www.gandaldf.com"] +__version__ = "3.0" +__bpydoc__ = """\ +""" + +# BLITZ3D EXPORTER 3.0 +# Copyright (C) 2009 by Diego "GaNDaLDF" Parisi - www.gandaldf.com +# Lightmap issue fixed by Capricorn 76 Pty. Ltd. - www.capricorn76.com +# Blender 2.63 compatiblity based on work by MTLZ, www.is06.com +# With changes by Marianne Gagnon, Joerg Henrichs and Vincent Lejeune, supertuxkart.sf.net (Copyright (C) 2011-2012) +# +# LICENSE: +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +bl_info = { + "name": "B3D (BLITZ3D) Model Exporter", + "description": "Exports a blender scene or object to the B3D (BLITZ3D) format", + "author": "Diego 'GaNDaLDF' Parisi, MTLZ (is06), Joerg Henrichs, Marianne Gagnon, Vincent Lejeune", + "version": (3,1), + "blender": (2, 5, 9), + "api": 31236, + "location": "File > Export", + "warning": '', # used for warning icon and text in addons panel + "wiki_url": "http://supertuxkart.sourceforge.net/Get_involved", + "tracker_url": "https://sourceforge.net/apps/trac/supertuxkart/", + "category": "Import-Export"} + + +import bpy +import sys,os,os.path,struct,math,string +import mathutils +import math + +if not hasattr(sys,"argv"): sys.argv = ["???"] + + +#Global Stacks +b3d_parameters = {} +texture_flags = [] +texs_stack = {} +brus_stack = [] +vertex_groups = [] +bone_stack = {} +keys_stack = [] + +texture_count = 0 + +# bone_stack indices constants +BONE_PARENT_MATRIX = 0 +BONE_PARENT = 1 +BONE_ITSELF = 2 + +# texture stack indices constants +TEXTURE_ID = 0 +TEXTURE_FLAGS = 1 + +per_face_vertices = {} + +the_scene = None + +#Transformation Matrix +TRANS_MATRIX = mathutils.Matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) +BONE_TRANS_MATRIX = mathutils.Matrix([[-1,0,0,0],[0,0,-1,0],[0,-1,0,0],[0,0,0,1]]) + +DEBUG = False +PROGRESS = True +PROGRESS_VERBOSE = False + +tesselated_objects = {} + +#Support Functions +def write_int(value): + return struct.pack(" 48: + value = encoded[0:48] + binary_format = "<%ds"%(len(encoded)+1) + return struct.pack(binary_format, encoded) + +def write_chunk(name,value): + dummy = bytearray() + return dummy + name + write_int(len(value)) + value + +trimmed_paths = {} + +def getArmatureAnimationEnd(armature): + end_frame = 1 + if armature.animation_data.action: + ipo = armature.animation_data.action.fcurves + for curve in ipo: + if "pose" in curve.data_path: + end_frame = max(end_frame, curve.keyframe_points[-1].co[0]) + + for nla_track in armature.animation_data.nla_tracks: + if len(nla_track.strips) > 0: + end_frame = max(end_frame, nla_track.strips[-1].frame_end) + + return end_frame + +# ==== Write B3D File ==== +# (main exporter function) +def write_b3d_file(filename, objects=[]): + global texture_flags, texs_stack, trimmed_paths, tesselated_objects + global brus_stack, vertex_groups, bone_stack, keys_stack + + #Global Stacks + texture_flags = [] + texs_stack = {} + brus_stack = [] + vertex_groups = [] + bone_stack = [] + keys_stack = [] + trimmed_paths = {} + file_buf = bytearray() + temp_buf = bytearray() + tesselated_objects = {} + + import time + start = time.time() + + temp_buf += write_int(1) #Version + temp_buf += write_texs(objects) #TEXS + temp_buf += write_brus(objects) #BRUS + temp_buf += write_node(objects) #NODE + + if len(temp_buf) > 0: + file_buf += write_chunk(b"BB3D",temp_buf) + temp_buf = "" + + file = open(filename,'wb') + file.write(file_buf) + file.close() + + # free memory + trimmed_paths = {} + + end = time.time() + + print("Exported in", (end - start)) + + +def tesselate_if_needed(objdata): + if objdata not in tesselated_objects: + objdata.calc_tessface() + tesselated_objects[objdata] = True + return objdata + +def getUVTextures(obj_data): + # BMesh in blender 2.63 broke this + if bpy.app.version[1] >= 63: + return tesselate_if_needed(obj_data).tessface_uv_textures + else: + return obj_data.uv_textures + +def getFaces(obj_data): + # BMesh in blender 2.63 broke this + if bpy.app.version[1] >= 63: + return tesselate_if_needed(obj_data).tessfaces + else: + return obj_data.faces + +def getVertexColors(obj_data): + # BMesh in blender 2.63 broke this + if bpy.app.version[1] >= 63: + return tesselate_if_needed(obj_data).tessface_vertex_colors + else: + return obj_data.vertex_colors + +# ==== Write TEXS Chunk ==== +def write_texs(objects=[]): + global b3d_parameters + global trimmed_paths + global texture_count + texs_buf = bytearray() + temp_buf = bytearray() + layer_max = 0 + obj_count = 0 + set_wrote = 0 + + if objects: + exp_obj = objects + else: + if b3d_parameters.get("export-selected"): + exp_obj = [ob for ob in bpy.data.objects if ob.select] + else: + exp_obj = bpy.data.objects + + if PROGRESS: print(len(exp_obj),"TEXS") + + if PROGRESS_VERBOSE: progress = 0 + + for obj in exp_obj: + + if PROGRESS_VERBOSE: + progress = progress + 1 + if (progress % 10 == 0): print("TEXS",progress,"/",len(exp_obj)) + + if obj.type == "MESH": + set_count = 0 + set_wrote = 0 + #data = obj.getData(mesh = True) + data = obj.data + + # FIXME? + #orig_uvlayer = data.activeUVLayer + + layer_set = [[],[],[],[],[],[],[],[]] + + # 8 UV layers are supported + texture_flags.append([None,None,None,None,None,None,None,None]) + + #if len(data.getUVLayerNames()) <= 8: + uv_textures = getUVTextures(data) + if len(uv_textures) <= 8: + if len(uv_textures) > layer_max: + layer_max = len(uv_textures) + else: + layer_max = 8 + + for face in getFaces(data): + for iuvlayer,uvlayer in enumerate(uv_textures): + if iuvlayer < 8: + + # FIXME? + #data.activeUVLayer = uvlayer + + #layer_set[iuvlayer].append(face.uv) + new_data = None + try: + new_data = uvlayer.data[face.index].uv + except: + pass + + layer_set[iuvlayer].append( new_data ) + + for i in range(len(uv_textures)): + if set_wrote: + set_count += 1 + set_wrote = 0 + + for iuvlayer in range(i,len(uv_textures)): + if layer_set[i] == layer_set[iuvlayer]: + if texture_flags[obj_count][iuvlayer] is None: + if set_count == 0: + tex_flag = 1 + elif set_count == 1: + tex_flag = 65536 + elif set_count > 1: + tex_flag = 1 + if b3d_parameters.get("mipmap"): + enable_mipmaps=8 + else: + enable_mipmaps=0 + texture_flags[obj_count][iuvlayer] = tex_flag | enable_mipmaps + set_wrote = 1 + + for face in getFaces(data): + for iuvlayer,uvlayer in enumerate(uv_textures): + if iuvlayer < 8: + + if not (iuvlayer < len(uv_textures)): + continue + + # FIXME? + #data.activeUVLayer = uvlayer + + #if DEBUG: print("") + + img = getUVTextures(data)[iuvlayer].data[face.index].image + + if img: + + if img.filepath in trimmed_paths: + img_name = trimmed_paths[img.filepath] + else: + img_name = bpy.path.basename(img.filepath) + trimmed_paths[img.filepath] = img_name + + if not img_name in texs_stack: + texs_stack[img_name] = [len(texs_stack), texture_flags[obj_count][iuvlayer]] + temp_buf += write_string(img_name) #Texture File Name + temp_buf += write_int(texture_flags[obj_count][iuvlayer]) #Flags + temp_buf += write_int(2) #Blend + temp_buf += write_float(0) #X_Pos + temp_buf += write_float(0) #Y_Pos + temp_buf += write_float(1) #X_Scale + temp_buf += write_float(1) #Y_Scale + temp_buf += write_float(0) #Rotation + #else: + # if DEBUG: print(" ") + + #if DEBUG: print("") + + obj_count += 1 + + #FIXME? + #if orig_uvlayer: + # data.activeUVLayer = orig_uvlayer + + texture_count = layer_max + + if len(temp_buf) > 0: + texs_buf += write_chunk(b"TEXS",temp_buf) + temp_buf = "" + + return texs_buf + +# ==== Write BRUS Chunk ==== +def write_brus(objects=[]): + global b3d_parameters + global trimmed_paths + global texture_count + brus_buf = bytearray() + temp_buf = bytearray() + mat_count = 0 + obj_count = 0 + + if DEBUG: print("") + + if objects: + exp_obj = objects + else: + if b3d_parameters.get("export-selected"): + exp_obj = [ob for ob in bpy.data.objects if ob.select] + else: + exp_obj = bpy.data.objects + + if PROGRESS: print(len(exp_obj),"BRUS") + if PROGRESS_VERBOSE: progress = 0 + + for obj in exp_obj: + + if PROGRESS_VERBOSE: + progress += 1 + if (progress % 10 == 0): print("BRUS",progress,"/",len(exp_obj)) + + if obj.type == "MESH": + data = obj.data + + uv_textures = getUVTextures(data) + + if len(uv_textures) <= 0: + continue + + if DEBUG: print("") + + img_found = 0 + + for face in getFaces(data): + + face_stack = [] + + for iuvlayer,uvlayer in enumerate(uv_textures): + if iuvlayer < 8: + + img_id = -1 + + if face.index >= len(uv_textures[iuvlayer].data): + continue + + img = uv_textures[iuvlayer].data[face.index].image + + if not img: + continue + + img_found = 1 + + if img.filepath in trimmed_paths: + img_name = trimmed_paths[img.filepath] + else: + img_name = os.path.basename(img.filepath) + trimmed_paths[img.filepath] = img_name + + if DEBUG: print(" ") + + if img_name in texs_stack: + img_id = texs_stack[img_name][TEXTURE_ID] + + face_stack.insert(iuvlayer,img_id) + if DEBUG: print(" ") + + for i in range(len(face_stack),texture_count): + face_stack.append(-1) + + + if DEBUG: print(" ") + + if not img_found: + if data.materials: + if data.materials[face.material_index]: + mat_data = data.materials[face.material_index] + mat_colr = mat_data.diffuse_color[0] + mat_colg = mat_data.diffuse_color[1] + mat_colb = mat_data.diffuse_color[2] + mat_alpha = mat_data.alpha + mat_name = mat_data.name + + if not mat_name in brus_stack: + brus_stack.append(mat_name) + temp_buf += write_string(mat_name) #Brush Name + temp_buf += write_float(mat_colr) #Red + temp_buf += write_float(mat_colg) #Green + temp_buf += write_float(mat_colb) #Blue + temp_buf += write_float(mat_alpha) #Alpha + temp_buf += write_float(0) #Shininess + temp_buf += write_int(1) #Blend + if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)): + temp_buf += write_int(2) #Fx + else: + temp_buf += write_int(0) #Fx + + for i in face_stack: + temp_buf += write_int(i) #Texture ID + else: + if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0: + if not face_stack in brus_stack: + brus_stack.append(face_stack) + mat_count += 1 + temp_buf += write_string("Brush.%.3i"%mat_count) #Brush Name + temp_buf += write_float(1) #Red + temp_buf += write_float(1) #Green + temp_buf += write_float(1) #Blue + temp_buf += write_float(1) #Alpha + temp_buf += write_float(0) #Shininess + temp_buf += write_int(1) #Blend + temp_buf += write_int(2) #Fx + + for i in face_stack: + temp_buf += write_int(i) #Texture ID + else: # img_found + + if not face_stack in brus_stack: + brus_stack.append(face_stack) + mat_count += 1 + temp_buf += write_string("Brush.%.3i"%mat_count) #Brush Name + temp_buf += write_float(1) #Red + temp_buf += write_float(1) #Green + temp_buf += write_float(1) #Blue + temp_buf += write_float(1) #Alpha + temp_buf += write_float(0) #Shininess + temp_buf += write_int(1) #Blend + + if DEBUG: print(" ") + + if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0: + temp_buf += write_int(2) #Fx + else: + temp_buf += write_int(0) #Fx + + for i in face_stack: + temp_buf += write_int(i) #Texture ID + if DEBUG: print(" ") + + if DEBUG: print(" ") + + if DEBUG: print("") + + if DEBUG: print("") + obj_count += 1 + + #FIXME? + #if orig_uvlayer: + # data.activeUVLayer = orig_uvlayer + + if len(temp_buf) > 0: + brus_buf += write_chunk(b"BRUS",write_int(texture_count) + temp_buf) #N Texs + temp_buf = "" + + return brus_buf + +# ==== Write NODE Chunk ==== +def write_node(objects=[]): + global bone_stack + global keys_stack + global b3d_parameters + global the_scene + + root_buf = [] + node_buf = [] + main_buf = bytearray() + temp_buf = [] + obj_count = 0 + amb_light = 0 + + num_mesh = 0 + num_ligs = 0 + num_cams = 0 + num_lorc = 0 + #exp_scn = Blender.Scene.GetCurrent() + #exp_scn = the_scene + #exp_con = exp_scn.getRenderingContext() + + #first_frame = Blender.Draw.Create(exp_con.startFrame()) + #last_frame = Blender.Draw.Create(exp_con.endFrame()) + #num_frames = last_frame.val - first_frame.val + first_frame = the_scene.frame_start + + if DEBUG: print("") + + if objects: + exp_obj = objects + else: + if b3d_parameters.get("export-selected"): + exp_obj = [ob for ob in bpy.data.objects if ob.select] + else: + exp_obj = bpy.data.objects + + for obj in exp_obj: + if obj.type == "MESH": + num_mesh += 1 + if obj.type == "CAMERA": + num_cams += 1 + if obj.type == "LAMP": + num_ligs += 1 + + if b3d_parameters.get("cameras"): + num_lorc += num_cams + + if b3d_parameters.get("lights"): + num_lorc += 1 + num_lorc += num_ligs + + if num_mesh + num_lorc > 1: + exp_root = 1 + else: + exp_root = 0 + + if exp_root: + root_buf.append(write_string("ROOT")) #Node Name + + root_buf.append(write_float_triplet(0, 0, 0)) #Position X,Y,Z + root_buf.append(write_float_triplet(1, 1, 1)) #Scale X, Y, Z + root_buf.append(write_float_quad(1, 0, 0, 0)) #Rotation W, X, Y, Z + + if PROGRESS: progress = 0 + + for obj in exp_obj: + + if PROGRESS: + progress += 1 + print("NODE:",progress,"/",len(exp_obj),obj.name) + + if obj.type == "MESH": + + if DEBUG: print(" ") + + bone_stack = {} + keys_stack = [] + + anim_data = None + + # check if this object has an armature modifier + for curr_mod in obj.modifiers: + if curr_mod.type == 'ARMATURE': + arm = curr_mod.object + if arm is not None: + anim_data = arm.animation_data + + # check if this object has an armature parent (second way to do armature animations in blender) + if anim_data is None: + if obj.parent: + if obj.parent.type == "ARMATURE": + arm = obj.parent + if arm.animation_data: + anim_data = arm.animation_data + + if anim_data: + matrix = mathutils.Matrix() + + temp_buf.append(write_string(obj.name)) #Node Name + + position = matrix.to_translation() + temp_buf.append(write_float_triplet(position[0], position[1], position[2])) #Position X, Y, Z + + scale = matrix.to_scale() + temp_buf.append(write_float_triplet(scale[0], scale[2], scale[1])) #Scale X, Y, Z + + if DEBUG: print(" ") + + quat = matrix.to_quaternion() + quat.normalize() + + temp_buf.append(write_float_quad(quat.w, quat.x, quat.z, quat.y)) + else: + if b3d_parameters.get("local-space"): + matrix = TRANS_MATRIX.copy() + scale_matrix = mathutils.Matrix() + else: + matrix = obj.matrix_world*TRANS_MATRIX + scale_matrix = obj.matrix_world.copy() + + + if bpy.app.version[1] >= 62: + # blender 2.62 broke the API : Column-major access was changed to row-major access + tmp = mathutils.Vector([matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1]]) + matrix[0][1] = matrix[0][2] + matrix[1][1] = matrix[1][2] + matrix[2][1] = matrix[2][2] + matrix[3][1] = matrix[3][2] + + matrix[0][2] = tmp[0] + matrix[1][2] = tmp[1] + matrix[2][2] = tmp[2] + matrix[3][2] = tmp[3] + else: + tmp = mathutils.Vector(matrix[1]) + matrix[1] = matrix[2] + matrix[2] = tmp + + temp_buf.append(write_string(obj.name)) #Node Name + + #print("Matrix : ", matrix) + position = matrix.to_translation() + + temp_buf.append(write_float_triplet(position[0], position[2], position[1])) + + scale = scale_matrix.to_scale() + temp_buf.append(write_float_triplet(scale[0], scale[2], scale[1])) + + quat = matrix.to_quaternion() + quat.normalize() + + temp_buf.append(write_float_quad(quat.w, quat.x, quat.z, quat.y)) + + if DEBUG: + print(" ",position[0],position[2],position[1],"") + print(" ",scale[0],scale[1],scale[2],"") + print(" ", quat.w, quat.x, quat.y, quat.z, "") + + if anim_data: + the_scene.frame_set(1,subframe=0.0) + + arm_matrix = arm.matrix_world + + if b3d_parameters.get("local-space"): + arm_matrix = mathutils.Matrix() + + def read_armature(arm_matrix,bone,parent = None): + if (parent and not bone.parent.name == parent.name): + return + + matrix = mathutils.Matrix(bone.matrix) + + if parent: + + #print("==== "+bone.name+" ====") + a = (bone.matrix_local) + + #print("A : [%.2f %.2f %.2f %.2f]" % (a[0][0], a[0][1], a[0][2], a[0][3])) + #print(" [%.2f %.2f %.2f %.2f]" % (a[1][0], a[1][1], a[1][2], a[1][3])) + #print(" [%.2f %.2f %.2f %.2f]" % (a[2][0], a[2][1], a[2][2], a[2][3])) + #print(" [%.2f %.2f %.2f %.2f]" % (a[3][0], a[3][1], a[3][2], a[3][3])) + + b = (parent.matrix_local.inverted().to_4x4()) + + #print("B : [%.2f %.2f %.2f %.2f]" % (b[0][0], b[0][1], b[0][2], b[0][3])) + #print(" [%.2f %.2f %.2f %.2f]" % (b[1][0], b[1][1], b[1][2], b[1][3])) + #print(" [%.2f %.2f %.2f %.2f]" % (b[2][0], b[2][1], b[2][2], b[2][3])) + #print(" [%.2f %.2f %.2f %.2f]" % (b[3][0], b[3][1], b[3][2], b[3][3])) + + par_matrix = b * a + + transform = mathutils.Matrix([[1,0,0,0],[0,0,-1,0],[0,-1,0,0],[0,0,0,1]]) + par_matrix = transform*par_matrix*transform + + # FIXME: that's ugly, find a clean way to change the matrix..... + if bpy.app.version[1] >= 62: + # blender 2.62 broke the API : Column-major access was changed to row-major access + # TODO: test me + par_matrix[1][3] = -par_matrix[1][3] + par_matrix[2][3] = -par_matrix[2][3] + else: + par_matrix[3][1] = -par_matrix[3][1] + par_matrix[3][2] = -par_matrix[3][2] + + #c = par_matrix + #print("With parent") + #print("C : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3])) + + else: + + #print("==== "+bone.name+" ====") + #print("Without parent") + + m = arm_matrix*bone.matrix_local + + #c = arm.matrix_world + #print("A : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3])) + + #c = bone.matrix_local + #print("B : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3])) + + par_matrix = m*mathutils.Matrix([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) + + #c = par_matrix + #print("C : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3])) + #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3])) + + + bone_stack[bone.name] = [par_matrix,parent,bone] + + if bone.children: + for child in bone.children: read_armature(arm_matrix,child,bone) + + for bone in arm.data.bones.values(): + if not bone.parent: + read_armature(arm_matrix,bone) + + frame_count = first_frame + + last_frame = int(getArmatureAnimationEnd(arm)) + num_frames = last_frame - first_frame + + while frame_count <= last_frame: + + the_scene.frame_set(int(frame_count), subframe=0.0) + + if DEBUG: print(" ") + arm_pose = arm.pose + arm_matrix = arm.matrix_world + + transform = mathutils.Matrix([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) + arm_matrix = transform*arm_matrix + #arm_quat = arm_matrix.to_quaternion() + #arm_quat.normalize() + + for bone_name in arm.data.bones.keys(): + #bone_matrix = mathutils.Matrix(arm_pose.bones[bone_name].poseMatrix) + bone_matrix = mathutils.Matrix(arm_pose.bones[bone_name].matrix) + + #print("(outer loop) bone_matrix for",bone_name,"=", bone_matrix) + + #print(bone_name,":",bone_matrix) + + #bone_matrix = bpy.data.scenes[0].objects[0].pose.bones['Bone'].matrix + + for ibone in bone_stack: + + bone = bone_stack[ibone] + + if bone[BONE_ITSELF].name == bone_name: + + if DEBUG: print(" ") + + # == 2.4 exporter == + #if bone_stack[ibone][1]: + # par_matrix = Blender.Mathutils.Matrix(arm_pose.bones[bone_stack[ibone][1].name].poseMatrix) + # bone_matrix *= par_matrix.invert() + #else: + # if b3d_parameters.get("local-space"): + # bone_matrix *= TRANS_MATRIX + # else: + # bone_matrix *= arm_matrix + #bone_loc = bone_matrix.translationPart() + #bone_rot = bone_matrix.rotationPart().toQuat() + #bone_rot.normalize() + #bone_sca = bone_matrix.scalePart() + #keys_stack.append([frame_count - first_frame.val+1,bone_name,bone_loc,bone_sca,bone_rot]) + + # if has parent + if bone[BONE_PARENT]: + par_matrix = mathutils.Matrix(arm_pose.bones[bone[BONE_PARENT].name].matrix) + bone_matrix = par_matrix.inverted()*bone_matrix + else: + if b3d_parameters.get("local-space"): + bone_matrix = bone_matrix*mathutils.Matrix([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) + else: + + #if frame_count == 1: + # print("====",bone_name,"====") + # print("arm_matrix = ", arm_matrix) + # print("bone_matrix = ", bone_matrix) + + bone_matrix = arm_matrix*bone_matrix + + #if frame_count == 1: + # print("arm_matrix*bone_matrix", bone_matrix) + + + #print("bone_matrix =", bone_matrix) + + bone_sca = bone_matrix.to_scale() + bone_loc = bone_matrix.to_translation() + + # FIXME: silly tweaks to resemble the Blender 2.4 exporter output + if b3d_parameters.get("local-space"): + + bone_rot = bone_matrix.to_quaternion() + bone_rot.normalize() + + + if not bone[BONE_PARENT]: + tmp = bone_rot.z + bone_rot.z = bone_rot.y + bone_rot.y = tmp + + bone_rot.x = -bone_rot.x + else: + tmp = bone_loc.z + bone_loc.z = bone_loc.y + bone_loc.y = tmp + + else: + bone_rot = bone_matrix.to_quaternion() + bone_rot.normalize() + + # Sometimes to_quaternion exhibits precision issue with parent bone + # Use quaternion product instead of getting quaternion from the product. + #if not bone[BONE_PARENT]: + # bone_rot = arm_pose.bones[bone_name].matrix.to_quaternion() * arm_quat + # bone_rot.x = -bone_rot.x + # tmp = bone_rot.z + # bone_rot.z = bone_rot.y + # bone_rot.y = tmp + + keys_stack.append([frame_count - first_frame+1, bone_name, bone_loc, bone_sca, bone_rot]) + if DEBUG: print(" ", bone_loc, "") + if DEBUG: print(" ", bone_rot, "") + if DEBUG: print(" ", bone_sca, "") + if DEBUG: print(" ") + + frame_count += 1 + + if DEBUG: print(" ") + + #Blender.Set("curframe",0) + #Blender.Window.Redraw() + + temp_buf.append(write_node_mesh(obj,obj_count,anim_data,exp_root)) #NODE MESH + + if anim_data: + temp_buf.append(write_node_anim(num_frames)) #NODE ANIM + + for ibone in bone_stack: + if not bone_stack[ibone][BONE_PARENT]: + temp_buf.append(write_node_node(ibone)) #NODE NODE + + obj_count += 1 + + if len(temp_buf) > 0: + node_buf.append(write_chunk(b"NODE",b"".join(temp_buf))) + temp_buf = [] + + if DEBUG: print(" ") + + if b3d_parameters.get("cameras"): + if obj.type == "CAMERA": + data = obj.data + matrix = obj.getMatrix("worldspace") + matrix *= TRANS_MATRIX + + if data.type == "ORTHO": + cam_type = 2 + cam_zoom = round(data.scale,4) + else: + cam_type = 1 + cam_zoom = round(data.lens,4) + + cam_near = round(data.clipStart,4) + cam_far = round(data.clipEnd,4) + + node_name = ("CAMS"+"\n%s"%obj.name+"\n%s"%cam_type+\ + "\n%s"%cam_zoom+"\n%s"%cam_near+"\n%s"%cam_far) + temp_buf.append(write_string(node_name)) #Node Name + + position = matrix.translation_part() + temp_buf.append(write_float_triplet(-position[0], position[1], position[2])) + + scale = matrix.scale_part() + temp_buf.append(write_float_triplet(scale[0], scale[1], scale[2])) + + matrix *= mathutils.Matrix.Rotation(180,4,'Y') + quat = matrix.to_quat() + quat.normalize() + + temp_buf.append(write_float_quad(quat.w, quat.x, quat.y, -quat.z)) + + if len(temp_buf) > 0: + node_buf.append(write_chunk(b"NODE",b"".join(temp_buf))) + temp_buf = [] + + if b3d_parameters.get("lights"): + if amb_light == 0: + data = Blender.World.GetCurrent() + + amb_light = 1 + amb_color = (int(data.amb[2]*255) |(int(data.amb[1]*255) << 8) | (int(data.amb[0]*255) << 16)) + + node_name = (b"AMBI"+"\n%s"%amb_color) + temp_buf.append(write_string(node_name)) #Node Name + + temp_buf.append(write_float_triplet(0, 0, 0)) #Position X, Y, Z + temp_buf.append(write_float_triplet(1, 1, 1)) #Scale X, Y, Z + temp_buf.append(write_float_quad(1, 0, 0, 0)) #Rotation W, X, Y, Z + + if len(temp_buf) > 0: + node_buf.append(write_chunk(b"NODE",b"".join(temp_buf))) + temp_buf = [] + + if obj.type == "LAMP": + data = obj.getData() + matrix = obj.getMatrix("worldspace") + matrix *= TRANS_MATRIX + + if data.type == 0: + lig_type = 2 + elif data.type == 2: + lig_type = 3 + else: + lig_type = 1 + + lig_angle = round(data.spotSize,4) + lig_color = (int(data.b*255) |(int(data.g*255) << 8) | (int(data.r*255) << 16)) + lig_range = round(data.dist,4) + + node_name = ("LIGS"+"\n%s"%obj.name+"\n%s"%lig_type+\ + "\n%s"%lig_angle+"\n%s"%lig_color+"\n%s"%lig_range) + temp_buf.append(write_string(node_name)) #Node Name + + position = matrix.translation_part() + temp_buf.append(write_float_triplet(-position[0], position[1], position[2])) + if DEBUG: print(" ",-position[0],position[1],position[2],"") + + scale = matrix.scale_part() + temp_buf.append(write_float_triplet(scale[0], scale[1], scale[2])) + + if DEBUG: print(" ",scale[0],scale[1],scale[2],"") + + matrix *= mathutils.Matrix.Rotation(180,4,'Y') + quat = matrix.toQuat() + quat.normalize() + + temp_buf.append(write_float_quad(quat.w, quat.x, quat.y, -quat.z)) + if DEBUG: print(" ", quat.w, quat.x, quat.y, quat.z, "") + + if len(temp_buf) > 0: + node_buf.append(write_chunk(b"NODE","b".join(temp_buf))) + temp_buf = [] + + if len(node_buf) > 0: + if exp_root: + main_buf += write_chunk(b"NODE",b"".join(root_buf) + b"".join(node_buf)) + else: + main_buf += b"".join(node_buf) + + node_buf = [] + root_buf = [] + + if DEBUG: print("") + + return main_buf + +# ==== Write NODE MESH Chunk ==== +def write_node_mesh(obj,obj_count,arm_action,exp_root): + global vertex_groups + vertex_groups = [] + mesh_buf = bytearray() + temp_buf = bytearray() + + if arm_action: + data = obj.data + elif b3d_parameters.get("apply-modifiers"): + data = obj.to_mesh(the_scene, True, 'PREVIEW') # Apply modifiers + else: + data = obj.data + + temp_buf += write_int(-1) #Brush ID + temp_buf += write_node_mesh_vrts(obj, data, obj_count, arm_action, exp_root) #NODE MESH VRTS + temp_buf += write_node_mesh_tris(obj, data, obj_count, arm_action, exp_root) #NODE MESH TRIS + + if len(temp_buf) > 0: + mesh_buf += write_chunk(b"MESH",temp_buf) + temp_buf = "" + + return mesh_buf + +def build_vertex_groups(data): + for f in getFaces(data): + for v in f.vertices: + vertex_groups.append({}) + + +# ==== Write NODE MESH VRTS Chunk ==== +def write_node_mesh_vrts(obj, data, obj_count, arm_action, exp_root): + vrts_buf = bytearray() + temp_buf = [] + obj_flags = 0 + + #global time_in_a + #global time_in_b + #global time_in_b1 + #global time_in_b2 + #global time_in_b3 + #global time_in_b4 + + #data = obj.getData(mesh = True) + global the_scene + + # FIXME: port to 2.5 API? + #orig_uvlayer = data.activeUVLayer + + if b3d_parameters.get("vertex-normals"): + obj_flags += 1 + + #if b3d_parameters.get("vertex-colors") and data.getColorLayerNames(): + if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0: + obj_flags += 2 + + temp_buf.append(write_int(obj_flags)) #Flags + #temp_buf += write_int(len(data.getUVLayerNames())) #UV Set + temp_buf.append(write_int(len(getUVTextures(data)))) #UV Set + temp_buf.append(write_int(2)) #UV Set Size + + # ---- Prepare the mesh "stack" + build_vertex_groups(data) + + # ---- Fill the mesh "stack" + if DEBUG: print("") + if DEBUG: print(" \n") + + ivert = -1 + + + #if PROGRESS_VERBOSE: + # progress = 0 + # print(" vertex_groups, face:",0,"/",len(getFaces(data))) + + the_scene.frame_set(1,subframe=0.0) + + if b3d_parameters.get("local-space"): + mesh_matrix = mathutils.Matrix() + else: + mesh_matrix = obj.matrix_world.copy() + + #import time + + uvdata = getUVTextures(data) + uv_layers_count = len(uvdata) + uv_textures = {} + weldable_vertices = {} + vertex_id_mapping = {} + for face in getFaces(data): + for vertex_index,vert in enumerate(face.vertices): + #for id,vert in enumerate(data.vertices): + + if vert not in uv_textures: + uv_textures[vert] = {} + if face not in uv_textures[vert]: + uv_textures[vert][face] = {} + + for iuvlayer in range(uv_layers_count): + if vertex_index == 0: + uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv1 + elif vertex_index == 1: + uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv2 + elif vertex_index == 2: + uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv3 + elif vertex_index == 3: + uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv4 + + for idVertex, vertexFaces in uv_textures.items(): + is_weldable = True + + for iuvlayer in range(uv_layers_count): + u = None + v = None + for idFace, face in vertexFaces.items(): + if iuvlayer not in face: + continue + faceuvlayer = face[iuvlayer] + if u is None: + u = round(faceuvlayer[0]*1000) + v = round(faceuvlayer[1]*1000) + else: + if round(faceuvlayer[0] * 1000) != u or round(faceuvlayer[1]*1000) != v: + is_weldable = False + break + if not is_weldable: + break + + weldable_vertices[idVertex] = is_weldable + + + #print('uv_textures', uv_textures) + #print('weldable_vertices', weldable_vertices) + + for face in getFaces(data): + + if DEBUG: print(" ") + + #if PROGRESS_VERBOSE: + # progress += 1 + # if (progress % 50 == 0): print(" vertex_groups, face:",progress,"/",len(data.faces)) + + per_face_vertices[face.index] = [] + + for vertex_id,vert in enumerate(face.vertices): + + if vert in weldable_vertices and weldable_vertices[vert] and vert in vertex_id_mapping: + per_face_vertices[face.index].append(vertex_id_mapping[vert]) + continue + + ivert += 1 + vertex_id_mapping[vert] = ivert + per_face_vertices[face.index].append(ivert) + + #a = time.time() + + if arm_action: + v = mesh_matrix * data.vertices[vert].co + vert_matrix = mathutils.Matrix.Translation(v) + else: + vert_matrix = mathutils.Matrix.Translation(data.vertices[vert].co) + + vert_matrix *= TRANS_MATRIX + vcoord = vert_matrix.to_translation() + + temp_buf.append(write_float_triplet(vcoord.x, vcoord.z, vcoord.y)) + + #b = time.time() + #time_in_a += b - a + + if b3d_parameters.get("vertex-normals"): + norm_matrix = mathutils.Matrix.Translation(data.vertices[vert].normal) + + if arm_action: + norm_matrix *= mesh_matrix + + norm_matrix *= TRANS_MATRIX + normal_vector = norm_matrix.to_translation() + + temp_buf.append(write_float_triplet(normal_vector.x, #NX + normal_vector.z, #NY + normal_vector.y)) #NZ + + #c = time.time() + #time_in_b += c - b + + if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0: + vertex_colors = getVertexColors(data) + if vertex_id == 0: + vcolor = vertex_colors[0].data[face.index].color1 + elif vertex_id == 1: + vcolor = vertex_colors[0].data[face.index].color2 + elif vertex_id == 2: + vcolor = vertex_colors[0].data[face.index].color3 + elif vertex_id == 3: + vcolor = vertex_colors[0].data[face.index].color4 + + valpha = 1.0 + if (len(vertex_colors) > 1): + if vertex_id == 0: + valpha = vertex_colors[1].data[face.index].color1.r + elif vertex_id == 1: + valpha = vertex_colors[1].data[face.index].color2.r + elif vertex_id == 2: + valpha = vertex_colors[1].data[face.index].color3.r + elif vertex_id == 3: + valpha = vertex_colors[1].data[face.index].color4.r + + temp_buf.append(write_float_quad(vcolor.r, #R + vcolor.g, #G + vcolor.b, #B + valpha)) #A (FIXME?) + + #d = time.time() + #time_in_b1 += d - c + + for vg in obj.vertex_groups: + w = 0.0 + try: + w = vg.weight(vert) + except: + pass + vertex_groups[ivert][vg.name] = w + + #e = time.time() + #time_in_b2 += e - d + + for iuvlayer in range(uv_layers_count): + uv = [10, 10] + if vert in uv_textures: + vertex_uv = uv_textures[vert] + + if face in vertex_uv: + vertex_face_uv = vertex_uv[face] + + if iuvlayer in vertex_face_uv: + uv = vertex_face_uv[iuvlayer] + + temp_buf.append(write_float_couple(uv[0], 1-uv[1]) ) # U, V + + + + if DEBUG: print("") + + #c = time.time() + #time_in_b += c - b + #print("time_in_a = ",time_in_a) + #print("time_in_b = ",time_in_b) + #print("time_in_b1 = ", time_in_b1) + #print("time_in_b2 = ", time_in_b2) + #print("time_in_b3 = ", time_in_b3) + #print("time_in_b4 = ", time_in_b4) + + if len(temp_buf) > 0: + vrts_buf += write_chunk(b"VRTS",b"".join(temp_buf)) + temp_buf = [] + + return vrts_buf + +# ==== Write NODE MESH TRIS Chunk ==== +def write_node_mesh_tris(obj, data, obj_count,arm_action,exp_root): + + global texture_count + + #FIXME? + #orig_uvlayer = data.activeUVLayer + + # An dictoriary that maps all brush-ids to a list of faces + # using this brush. This helps to sort the triangles by + # brush, creating less mesh buffer in irrlicht. + dBrushId2Face = {} + + if DEBUG: print("") + + for face in getFaces(data): + img_found = 0 + face_stack = [] + + uv_textures = getUVTextures(data) + uv_layer_count = len(uv_textures) + for iuvlayer,uvlayer in enumerate(uv_textures): + if iuvlayer < 8: + + if iuvlayer >= uv_layer_count: + continue + + img_id = -1 + + img = uv_textures[iuvlayer].data[face.index].image + if img: + + if img.filepath in trimmed_paths: + img_name = trimmed_paths[img.filepath] + else: + img_name = os.path.basename(img.filepath) + trimmed_paths[img.filepath] = img_name + + img_found = 1 + if img_name in texs_stack: + img_id = texs_stack[img_name][TEXTURE_ID] + + face_stack.insert(iuvlayer,img_id) + + for i in range(len(face_stack),texture_count): + face_stack.append(-1) + + if img_found == 0: + brus_id = -1 + if data.materials and data.materials[face.material_index]: + mat_name = data.materials[face.material_index].name + for i in range(len(brus_stack)): + if brus_stack[i] == mat_name: + brus_id = i + break + else: + for i in range(len(brus_stack)): + if brus_stack[i] == face_stack: + brus_id = i + break + else: + brus_id = -1 + for i in range(len(brus_stack)): + if brus_stack[i] == face_stack: + brus_id = i + break + if brus_id == -1: + print("Cannot find in brus stack : ", face_stack) + + if brus_id in dBrushId2Face: + dBrushId2Face[brus_id].append(face) + else: + dBrushId2Face[brus_id] = [face] + + if DEBUG: print(" ") + + tris_buf = bytearray() + + if DEBUG: print("") + if DEBUG: print(" ") + + if PROGRESS_VERBOSE: progress = 0 + + for brus_id in dBrushId2Face.keys(): + + if PROGRESS_VERBOSE: + progress += 1 + print("BRUS:",progress,"/",len(dBrushId2Face.keys())) + + temp_buf = [write_int(brus_id)] #Brush ID + + if DEBUG: print(" ") + + if PROGRESS_VERBOSE: progress2 = 0 + + for face in dBrushId2Face[brus_id]: + + if PROGRESS_VERBOSE: + progress2 += 1 + if (progress2 % 50 == 0): print(" TRIS:",progress2,"/",len(dBrushId2Face[brus_id])) + + vertices = per_face_vertices[face.index] + + temp_buf.append(write_int(vertices[2])) #A + temp_buf.append(write_int(vertices[1])) #B + temp_buf.append(write_int(vertices[0])) #C + + if DEBUG: print(" ") + + if len(face.vertices) == 4: + temp_buf.append(write_int(vertices[3])) #A + temp_buf.append(write_int(vertices[2])) #B + temp_buf.append(write_int(vertices[0])) #C + if DEBUG: print(" ") + + if DEBUG: print(" ") + tris_buf += write_chunk(b"TRIS", b"".join(temp_buf)) + + return tris_buf + +# ==== Write NODE ANIM Chunk ==== +def write_node_anim(num_frames): + anim_buf = bytearray() + temp_buf = bytearray() + + temp_buf += write_int(0) #Flags + temp_buf += write_int(num_frames) #Frames + temp_buf += write_float(60) #FPS + + if len(temp_buf) > 0: + anim_buf += write_chunk(b"ANIM",temp_buf) + temp_buf = "" + + return anim_buf + +# ==== Write NODE NODE Chunk ==== +def write_node_node(ibone): + node_buf = bytearray() + temp_buf = [] + + bone = bone_stack[ibone] + + matrix = bone[BONE_PARENT_MATRIX] + temp_buf.append(write_string(bone[BONE_ITSELF].name)) #Node Name + + + + # FIXME: we should use the same matrix format everywhere to not require this + + position = matrix.to_translation() + if bone[BONE_PARENT]: + temp_buf.append(write_float_triplet(-position[0], position[2], position[1])) + else: + temp_buf.append(write_float_triplet(position[0], position[2], position[1])) + + + scale = matrix.to_scale() + temp_buf.append(write_float_triplet(scale[0], scale[2], scale[1])) + + quat = matrix.to_quaternion() + quat.normalize() + + temp_buf.append(write_float_quad(quat.w, quat.x, quat.z, quat.y)) + + temp_buf.append(write_node_bone(ibone)) + temp_buf.append(write_node_keys(ibone)) + + for iibone in bone_stack: + if bone_stack[iibone][BONE_PARENT] == bone_stack[ibone][BONE_ITSELF]: + temp_buf.append(write_node_node(iibone)) + + if len(temp_buf) > 0: + node_buf += write_chunk(b"NODE", b"".join(temp_buf)) + temp_buf = [] + + return node_buf + +# ==== Write NODE BONE Chunk ==== +def write_node_bone(ibone): + bone_buf = bytearray() + temp_buf = [] + + my_name = bone_stack[ibone][BONE_ITSELF].name + + for ivert in range(len(vertex_groups)): + if my_name in vertex_groups[ivert]: + vert_influ = vertex_groups[ivert][my_name] + #if DEBUG: print(" ") + temp_buf.append(write_int(ivert)) # Face Vertex ID + temp_buf.append(write_float(vert_influ)) #Weight + + bone_buf += write_chunk(b"BONE", b"".join(temp_buf)) + temp_buf = [] + + return bone_buf + +# ==== Write NODE KEYS Chunk ==== +def write_node_keys(ibone): + keys_buf = bytearray() + temp_buf = [] + + temp_buf.append(write_int(7)) #Flags + + my_name = bone_stack[ibone][BONE_ITSELF].name + + for ikeys in range(len(keys_stack)): + if keys_stack[ikeys][1] == my_name: + temp_buf.append(write_int(keys_stack[ikeys][0])) #Frame + + position = keys_stack[ikeys][2] + # FIXME: we should use the same matrix format everywhere and not require this + if b3d_parameters.get("local-space"): + if bone_stack[ibone][BONE_PARENT]: + temp_buf.append(write_float_triplet(-position[0], position[2], position[1])) + else: + temp_buf.append(write_float_triplet(position[0], position[2], position[1])) + else: + temp_buf.append(write_float_triplet(-position[0], position[1], position[2])) + + scale = keys_stack[ikeys][3] + temp_buf.append(write_float_triplet(scale[0], scale[1], scale[2])) + + quat = keys_stack[ikeys][4] + quat.normalize() + + temp_buf.append(write_float_quad(quat.w, -quat.x, quat.y, quat.z)) + #break + + keys_buf += write_chunk(b"KEYS",b"".join(temp_buf)) + temp_buf = [] + + return keys_buf + + +# ==== CONFIRM OPERATOR ==== +class B3D_Confirm_Operator(bpy.types.Operator): + bl_idname = ("screen.b3d_confirm") + bl_label = ("File Exists, Overwrite?") + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_props_dialog(self) + + def execute(self, context): + write_b3d_file(B3D_Confirm_Operator.filepath) + return {'FINISHED'} + + +#class ObjectListItem(bpy.types.PropertyGroup): +# id = bpy.props.IntProperty(name="ID") +# +#bpy.utils.register_class(ObjectListItem) + +# ==== EXPORT OPERATOR ==== + +class B3D_Export_Operator(bpy.types.Operator): + bl_idname = ("screen.b3d_export") + bl_label = ("B3D Export") + filepath = bpy.props.StringProperty(subtype="FILE_PATH") + + selected = bpy.props.BoolProperty(name="Export Selected Only", default=False) + vnormals = bpy.props.BoolProperty(name="Export Vertex Normals", default=True) + vcolors = bpy.props.BoolProperty(name="Export Vertex Colors", default=True) + cameras = bpy.props.BoolProperty(name="Export Cameras", default=False) + lights = bpy.props.BoolProperty(name="Export Lights", default=False) + mipmap = bpy.props.BoolProperty(name="Mipmap", default=False) + localsp = bpy.props.BoolProperty(name="Use Local Space Coords", default=False) + applymodifiers = bpy.props.BoolProperty(name="Apply modifiers", default=True) + + overwrite_without_asking = bpy.props.BoolProperty(name="Overwrite without asking", default=False) + + #skip_dialog = False + + #objects = bpy.props.CollectionProperty(type=ObjectListItem, options={'HIDDEN'}) + + def invoke(self, context, event): + blend_filepath = context.blend_data.filepath + if not blend_filepath: + blend_filepath = "Untitled.b3d" + else: + blend_filepath = os.path.splitext(blend_filepath)[0] + ".b3d" + self.filepath = blend_filepath + + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + + global b3d_parameters + global the_scene + b3d_parameters["export-selected"] = self.selected + b3d_parameters["vertex-normals" ] = self.vnormals + b3d_parameters["vertex-colors" ] = self.vcolors + b3d_parameters["cameras" ] = self.cameras + b3d_parameters["lights" ] = self.lights + b3d_parameters["mipmap" ] = self.mipmap + b3d_parameters["local-space" ] = self.localsp + b3d_parameters["apply-modifiers"] = self.applymodifiers + + the_scene = context.scene + + if self.filepath == "": + return {'FINISHED'} + + if not self.filepath.endswith(".b3d"): + self.filepath += ".b3d" + + print("EXPORT", self.filepath," vcolor = ", self.vcolors) + + obj_list = [] + try: + # FIXME: silly and ugly hack, the list of objects to export is passed through + # a custom scene property + obj_list = context.scene.obj_list + except: + pass + + if len(obj_list) > 0: + + #objlist = [] + #for a in self.objects: + # objlist.append(bpy.data.objects[a.id]) + # + #write_b3d_file(self.filepath, obj_list) + + write_b3d_file(self.filepath, obj_list) + else: + if os.path.exists(self.filepath) and not self.overwrite_without_asking: + #self.report({'ERROR'}, "File Exists") + B3D_Confirm_Operator.filepath = self.filepath + bpy.ops.screen.b3d_confirm('INVOKE_DEFAULT') + return {'FINISHED'} + else: + write_b3d_file(self.filepath) + return {'FINISHED'} + + +# Add to a menu +def menu_func_export(self, context): + global the_scene + the_scene = context.scene + self.layout.operator(B3D_Export_Operator.bl_idname, text="B3D (.b3d)") + +def register(): + bpy.types.INFO_MT_file_export.append(menu_func_export) + bpy.utils.register_module(__name__) + +def unregister(): + bpy.types.INFO_MT_file_export.remove(menu_func_export) + +if __name__ == "__main__": + register() +