text:
#include "OgreStableHeaders.h"
#include "OgreStaticGeometry.h"
#include "OgreEntity.h"
#include "OgreEdgeListBuilder.h"
#include "OgreLodStrategy.h"
#include "OgreSubEntity.h"

namespace Ogre {

    #define REGION_RANGE 1024
    #define REGION_HALF_RANGE 512
    #define REGION_MAX_INDEX 511
    #define REGION_MIN_INDEX -512

    //--------------------------------------------------------------------------
    StaticGeometry::StaticGeometry(SceneManager* owner, const String& name):
        mOwner(owner),
        mName(name),
        mUpperDistance(0.0f),
        mSquaredUpperDistance(0.0f),
        mCastShadows(false),
        mRegionDimensions(Vector3(1000,1000,1000)),
        mHalfRegionDimensions(Vector3(500,500,500)),
        mOrigin(Vector3(0,0,0)),
        mVisible(true),
        mRenderQueueID(RENDER_QUEUE_MAIN),
        mRenderQueueIDSet(false),
        mVisibilityFlags(Ogre::MovableObject::getDefaultVisibilityFlags())
    {
    }
    //--------------------------------------------------------------------------
    StaticGeometry::~StaticGeometry()
    {
        reset();
    }
    //--------------------------------------------------------------------------
    StaticGeometry::Region* StaticGeometry::getRegion(const AxisAlignedBox& bounds,
        bool autoCreate)
    {
        if (bounds.isNull())
            return 0;

        // Get the region which has the largest overlapping volume
        const Vector3 min = bounds.getMinimum();
        const Vector3 max = bounds.getMaximum();

        // Get the min and max region indexes
        ushort minx, miny, minz;
        ushort maxx, maxy, maxz;
        getRegionIndexes(min, minx, miny, minz);
        getRegionIndexes(max, maxx, maxy, maxz);
        Real maxVolume = 0.0f;
        ushort finalx = 0, finaly = 0, finalz = 0;
        for (ushort x = minx; x <= maxx; ++x)
        {
            for (ushort y = miny; y <= maxy; ++y)
            {
                for (ushort z = minz; z <= maxz; ++z)
                {
                    Real vol = getVolumeIntersection(bounds, x, y, z);
                    if (vol > maxVolume)
                    {
                        maxVolume = vol;
                        finalx = x;
                        finaly = y;
                        finalz = z;
                    }

                }
            }
        }

        assert(maxVolume > 0.0f &&
            "Static geometry: Problem determining closest volume match!");

        return getRegion(finalx, finaly, finalz, autoCreate);

    }
    //--------------------------------------------------------------------------
    Real StaticGeometry::getVolumeIntersection(const AxisAlignedBox& box,
        ushort x, ushort y, ushort z)
    {
        // Get bounds of indexed region
        AxisAlignedBox regionBounds = getRegionBounds(x, y, z);
        AxisAlignedBox intersectBox = regionBounds.intersection(box);
        // return a 'volume' which ignores zero dimensions
        // since we only use this for relative comparisons of the same bounds
        // this will still be internally consistent
        Vector3 boxdiff = box.getMaximum() - box.getMinimum();
        Vector3 intersectDiff = intersectBox.getMaximum() - intersectBox.getMinimum();

        return (boxdiff.x == 0 ? 1 : intersectDiff.x) *
            (boxdiff.y == 0 ? 1 : intersectDiff.y) *
            (boxdiff.z == 0 ? 1 : intersectDiff.z);

    }
    //--------------------------------------------------------------------------
    AxisAlignedBox StaticGeometry::getRegionBounds(ushort x, ushort y, ushort z)
    {
        Vector3 min(
            ((Real)x - REGION_HALF_RANGE) * mRegionDimensions.x + mOrigin.x,
            ((Real)y - REGION_HALF_RANGE) * mRegionDimensions.y + mOrigin.y,
            ((Real)z - REGION_HALF_RANGE) * mRegionDimensions.z + mOrigin.z
            );
        Vector3 max = min + mRegionDimensions;
        return AxisAlignedBox(min, max);
    }
    //--------------------------------------------------------------------------
    Vector3 StaticGeometry::getRegionCentre(ushort x, ushort y, ushort z)
    {
        return Vector3(
            ((Real)x - REGION_HALF_RANGE) * mRegionDimensions.x + mOrigin.x
                + mHalfRegionDimensions.x,
            ((Real)y - REGION_HALF_RANGE) * mRegionDimensions.y + mOrigin.y
                + mHalfRegionDimensions.y,
            ((Real)z - REGION_HALF_RANGE) * mRegionDimensions.z + mOrigin.z
                + mHalfRegionDimensions.z
            );
    }
    //--------------------------------------------------------------------------
    StaticGeometry::Region* StaticGeometry::getRegion(
            ushort x, ushort y, ushort z, bool autoCreate)
    {
        uint32 index = packIndex(x, y, z);
        Region* ret = getRegion(index);
        if (!ret && autoCreate)
        {
            // Make a name
            StringStream str;
            str << mName << ":" << index;
            // Calculate the region centre
            Vector3 centre = getRegionCentre(x, y, z);
            ret = OGRE_NEW Region(this, str.str(), mOwner, index, centre);
            mOwner->injectMovableObject(ret);
            ret->setVisible(mVisible);
            ret->setCastShadows(mCastShadows);
            if (mRenderQueueIDSet)
            {
                ret->setRenderQueueGroup(mRenderQueueID);
            }
            mRegionMap[index] = ret;
        }
        return ret;
    }
    //--------------------------------------------------------------------------
    StaticGeometry::Region* StaticGeometry::getRegion(uint32 index)
    {
        RegionMap::iterator i = mRegionMap.find(index);
        if (i != mRegionMap.end())
        {
            return i->second;
        }
        else
        {
            return 0;
        }

    }
    //--------------------------------------------------------------------------
    void StaticGeometry::getRegionIndexes(const Vector3& point,
        ushort& x, ushort& y, ushort& z)
    {
        // Scale the point into multiples of region and adjust for origin
        Vector3 scaledPoint = (point - mOrigin) / mRegionDimensions;

        // Round down to 'bottom left' point which represents the cell index
        int ix = Math::IFloor(scaledPoint.x);
        int iy = Math::IFloor(scaledPoint.y);
        int iz = Math::IFloor(scaledPoint.z);

        // Check bounds
        if (ix < REGION_MIN_INDEX || ix > REGION_MAX_INDEX
            || iy < REGION_MIN_INDEX || iy > REGION_MAX_INDEX
            || iz < REGION_MIN_INDEX || iz > REGION_MAX_INDEX)
        {
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "Point out of bounds",
                "StaticGeometry::getRegionIndexes");
        }
        // Adjust for the fact that we use unsigned values for simplicity
        // (requires less faffing about for negatives give 10-bit packing
        x = static_cast<ushort>(ix + REGION_HALF_RANGE);
        y = static_cast<ushort>(iy + REGION_HALF_RANGE);
        z = static_cast<ushort>(iz + REGION_HALF_RANGE);

    }
    //--------------------------------------------------------------------------
    uint32 StaticGeometry::packIndex(ushort x, ushort y, ushort z)
    {
        return x + (y << 10) + (z << 20);
    }
    //--------------------------------------------------------------------------
    StaticGeometry::Region* StaticGeometry::getRegion(const Vector3& point,
        bool autoCreate)
    {
        ushort x, y, z;
        getRegionIndexes(point, x, y, z);
        return getRegion(x, y, z, autoCreate);
    }
    //--------------------------------------------------------------------------
    AxisAlignedBox StaticGeometry::calculateBounds(VertexData* vertexData,
        const Vector3& position, const Quaternion& orientation,
        const Vector3& scale)
    {
        const VertexElement* posElem =
            vertexData->vertexDeclaration->findElementBySemantic(
                VES_POSITION);
        HardwareVertexBufferSharedPtr vbuf =
            vertexData->vertexBufferBinding->getBuffer(posElem->getSource());
        HardwareBufferLockGuard vbufLock(vbuf, HardwareBuffer::HBL_READ_ONLY);
        unsigned char* vertex = static_cast<unsigned char*>(vbufLock.pData);
        float* pFloat;

        Vector3 min = Vector3::ZERO, max = Vector3::UNIT_SCALE;
        bool first = true;

        for(size_t j = 0; j < vertexData->vertexCount; ++j, vertex += vbuf->getVertexSize())
        {
            posElem->baseVertexPointerToElement(vertex, &pFloat);

            Vector3 pt;

            pt.x = (*pFloat++);
            pt.y = (*pFloat++);
            pt.z = (*pFloat++);
            // Transform to world (scale, rotate, translate)
            pt = (orientation * (pt * scale)) + position;
            if (first)
            {
                min = max = pt;
                first = false;
            }
            else
            {
                min.makeFloor(pt);
                max.makeCeil(pt);
            }

        }
        return AxisAlignedBox(min, max);
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::addEntity(Entity* ent, const Vector3& position,
        const Quaternion& orientation, const Vector3& scale)
    {
        const MeshPtr& msh = ent->getMesh();
        // Validate
        if (msh->hasManualLodLevel())
        {
            LogManager::getSingleton().logWarning("(StaticGeometry): Manual LOD is not supported. "
                                                  "Using only highest LOD level for mesh " +
                                                  msh->getName());
        }

        AxisAlignedBox sharedWorldBounds;
        // queue this entities submeshes and choice of material
        // also build the lists of geometry to be used for the source of lods
        for (uint i = 0; i < ent->getNumSubEntities(); ++i)
        {
            SubEntity* se = ent->getSubEntity(i);
            QueuedSubMesh* q = OGRE_NEW QueuedSubMesh();

            // Get the geometry for this SubMesh
            q->submesh = se->getSubMesh();
            q->material = se->getMaterial();
            q->geometryLodList = determineGeometry(q->submesh);
            q->orientation = orientation;
            q->position = position;
            q->scale = scale;
            // Determine the bounds based on the highest LOD
            q->worldBounds = calculateBounds(
                (*q->geometryLodList)[0].vertexData,
                    position, orientation, scale);

            mQueuedSubMeshes.push_back(q);
        }
    }
    //--------------------------------------------------------------------------
    StaticGeometry::SubMeshLodGeometryLinkList*
    StaticGeometry::determineGeometry(SubMesh* sm)
    {
        OgreAssert(sm->indexData->indexBuffer, "currently only works with indexed geometry");
        // First, determine if we've already seen this submesh before
        SubMeshGeometryLookup::iterator i =
            mSubMeshGeometryLookup.find(sm);
        if (i != mSubMeshGeometryLookup.end())
        {
            return i->second;
        }
        // Otherwise, we have to create a new one
        SubMeshLodGeometryLinkList* lodList = OGRE_NEW_T(SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY)();
        mSubMeshGeometryLookup[sm] = lodList;
        ushort numLods = sm->parent->hasManualLodLevel() ? 1 :
            sm->parent->getNumLodLevels();
        lodList->resize(numLods);
        for (ushort lod = 0; lod < numLods; ++lod)
        {
            SubMeshLodGeometryLink& geomLink = (*lodList)[lod];
            IndexData *lodIndexData;
            if (lod == 0)
            {
                lodIndexData = sm->indexData;
            }
            else
            {
                lodIndexData = sm->mLodFaceList[lod - 1];
            }
            // Can use the original mesh geometry?
            if (sm->useSharedVertices)
            {
                if (sm->parent->getNumSubMeshes() == 1)
                {
                    // Ok, this is actually our own anyway
                    geomLink.vertexData = sm->parent->sharedVertexData;
                    geomLink.indexData = lodIndexData;
                }
                else
                {
                    // We have to split it
                    splitGeometry(sm->parent->sharedVertexData,
                        lodIndexData, &geomLink);
                }
            }
            else
            {
                if (lod == 0)
                {
                    // Ok, we can use the existing geometry; should be in full
                    // use by just this SubMesh
                    geomLink.vertexData = sm->vertexData;
                    geomLink.indexData = sm->indexData;
                }
                else
                {
                    // We have to split it
                    splitGeometry(sm->vertexData,
                        lodIndexData, &geomLink);
                }
            }
            assert (geomLink.vertexData->vertexStart == 0 &&
                "Cannot use vertexStart > 0 on indexed geometry due to "
                "rendersystem incompatibilities - see the docs!");
        }

        return lodList;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::splitGeometry(VertexData* vd, IndexData* id,
            StaticGeometry::SubMeshLodGeometryLink* targetGeomLink)
    {
        // Firstly we need to scan to see how many vertices are being used
        // and while we're at it, build the remap we can use later
        bool use32bitIndexes =
            id->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT;
        IndexRemap indexRemap;
        HardwareBufferLockGuard indexLock(id->indexBuffer,
                                          id->indexStart * id->indexBuffer->getIndexSize(),
                                          id->indexCount * id->indexBuffer->getIndexSize(),
                                          HardwareBuffer::HBL_READ_ONLY);
        if (use32bitIndexes)
        {
            buildIndexRemap(static_cast<uint32*>(indexLock.pData), id->indexCount, indexRemap);
        }
        else
        {
            buildIndexRemap(static_cast<uint16*>(indexLock.pData), id->indexCount, indexRemap);
        }
        indexLock.unlock();
        if (indexRemap.size() == vd->vertexCount)
        {
            // ha, complete usage after all
            targetGeomLink->vertexData = vd;
            targetGeomLink->indexData = id;
            return;
        }

        // Create the new vertex data records
        targetGeomLink->vertexData = vd->clone(false);
        // Convenience
        VertexData* newvd = targetGeomLink->vertexData;
        //IndexData* newid = targetGeomLink->indexData;
        // Update the vertex count
        newvd->vertexCount = indexRemap.size();

        size_t numvbufs = vd->vertexBufferBinding->getBufferCount();
        // Copy buffers from old to new
        for (unsigned short b = 0; b < numvbufs; ++b)
        {
            // Lock old buffer
            HardwareVertexBufferSharedPtr oldBuf =
                vd->vertexBufferBinding->getBuffer(b);
            // Create new buffer
            HardwareVertexBufferSharedPtr newBuf =
                HardwareBufferManager::getSingleton().createVertexBuffer(
                    oldBuf->getVertexSize(),
                    indexRemap.size(),
                    HardwareBuffer::HBU_STATIC);
            // rebind
            newvd->vertexBufferBinding->setBinding(b, newBuf);

            // Copy all the elements of the buffer across, by iterating over
            // the IndexRemap which describes how to move the old vertices
            // to the new ones. By nature of the map the remap is in order of
            // indexes in the old buffer, but note that we're not guaranteed to
            // address every vertex (which is kinda why we're here)
            HardwareBufferLockGuard oldBufLock(oldBuf, HardwareBuffer::HBL_READ_ONLY);
            HardwareBufferLockGuard newBufLock(newBuf, HardwareBuffer::HBL_DISCARD);
            size_t vertexSize = oldBuf->getVertexSize();
            // Buffers should be the same size
            assert (vertexSize == newBuf->getVertexSize());

            for (auto & r : indexRemap)
            {
                assert (r.first < oldBuf->getNumVertices());
                assert (r.second < newBuf->getNumVertices());

                uchar* pSrc = static_cast<uchar*>(oldBufLock.pData) + r.first * vertexSize;
                uchar* pDst = static_cast<uchar*>(newBufLock.pData) + r.second * vertexSize;
                memcpy(pDst, pSrc, vertexSize);
            }
        }

        // Now create a new index buffer
        HardwareIndexBufferSharedPtr ibuf =
            HardwareBufferManager::getSingleton().createIndexBuffer(
                id->indexBuffer->getType(), id->indexCount,
                HardwareBuffer::HBU_STATIC);

        HardwareBufferLockGuard srcIndexLock(id->indexBuffer,
                                             id->indexStart * id->indexBuffer->getIndexSize(),
                                             id->indexCount * id->indexBuffer->getIndexSize(),
                                             HardwareBuffer::HBL_READ_ONLY);
        HardwareBufferLockGuard dstIndexLock(ibuf, HardwareBuffer::HBL_DISCARD);
        if (use32bitIndexes)
        {
            uint32 *pSrc32 = static_cast<uint32*>(srcIndexLock.pData);
            uint32 *pDst32 = static_cast<uint32*>(dstIndexLock.pData);
            remapIndexes(pSrc32, pDst32, indexRemap, id->indexCount);
        }
        else
        {
            uint16 *pSrc16 = static_cast<uint16*>(srcIndexLock.pData);
            uint16 *pDst16 = static_cast<uint16*>(dstIndexLock.pData);
            remapIndexes(pSrc16, pDst16, indexRemap, id->indexCount);
        }
        srcIndexLock.unlock();
        dstIndexLock.unlock();

        targetGeomLink->indexData = OGRE_NEW IndexData();
        targetGeomLink->indexData->indexStart = 0;
        targetGeomLink->indexData->indexCount = id->indexCount;
        targetGeomLink->indexData->indexBuffer = ibuf;

        // Store optimised geometry for deallocation later
        OptimisedSubMeshGeometry *optGeom = OGRE_NEW OptimisedSubMeshGeometry();
        optGeom->indexData = targetGeomLink->indexData;
        optGeom->vertexData = targetGeomLink->vertexData;
        mOptimisedSubMeshGeometryList.push_back(optGeom);
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::addSceneNode(const SceneNode* node)
    {
        if(node->getCreator()->getRootSceneNode()->_getFullTransform() != Affine3::IDENTITY)
        {
            // otherwise it is applied twice
            LogManager::getSingleton().logError("StaticGeometry - Root SceneNode transform must be IDENTITY");
        }

        for (auto mobj : node->getAttachedObjects())
        {
            if (mobj->getMovableType() == "Entity")
            {
                addEntity(static_cast<Entity*>(mobj),
                    node->_getDerivedPosition(),
                    node->_getDerivedOrientation(),
                    node->_getDerivedScale());
            }
        }
        // Iterate through all the child-nodes
        for (auto c : node->getChildren())
        {
            // Add this subnode and its children...
            addSceneNode( static_cast<const SceneNode*>(c) );
        }
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::build(void)
    {
        // Make sure there's nothing from previous builds
        destroy();

        // Firstly allocate meshes to regions
        for (auto qsm : mQueuedSubMeshes)
        {
            Region* region = getRegion(qsm->worldBounds, true);
            region->assign(qsm);
        }
        bool stencilShadows = false;
        if (mCastShadows && mOwner->isShadowTechniqueStencilBased())
        {
            stencilShadows = true;
        }

        // Now tell each region to build itself
        for (auto & ri : mRegionMap)
        {
            ri.second->build(stencilShadows);

            // Set the visibility flags on these regions
            ri.second->setVisibilityFlags(mVisibilityFlags);
        }

    }
    //--------------------------------------------------------------------------
    void StaticGeometry::destroy(void)
    {
        // delete the regions
        for (auto & i : mRegionMap)
        {
            mOwner->extractMovableObject(i.second);
            OGRE_DELETE i.second;
        }
        mRegionMap.clear();
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::reset(void)
    {
        destroy();
        for (auto m : mQueuedSubMeshes)
        {
            OGRE_DELETE m;
        }
        mQueuedSubMeshes.clear();
        // Delete precached geoemtry lists
        for (SubMeshGeometryLookup::iterator l = mSubMeshGeometryLookup.begin();
            l != mSubMeshGeometryLookup.end(); ++l)
        {
            OGRE_DELETE_T(l->second, SubMeshLodGeometryLinkList, MEMCATEGORY_GEOMETRY);
        }
        mSubMeshGeometryLookup.clear();
        // Delete optimised geometry
        for (auto o : mOptimisedSubMeshGeometryList)
        {
            OGRE_DELETE o;
        }
        mOptimisedSubMeshGeometryList.clear();

    }
    //--------------------------------------------------------------------------
    void StaticGeometry::setVisible(bool visible)
    {
        mVisible = visible;
        // tell any existing regions
        for (auto & ri : mRegionMap)
        {
            ri.second->setVisible(visible);
        }
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::setCastShadows(bool castShadows)
    {
        mCastShadows = castShadows;
        // tell any existing regions
        for (auto & ri : mRegionMap)
        {
            ri.second->setCastShadows(castShadows);
        }

    }
    //--------------------------------------------------------------------------
    void StaticGeometry::setRenderQueueGroup(uint8 queueID)
    {
        assert(queueID <= RENDER_QUEUE_MAX && "Render queue out of range!");
        mRenderQueueIDSet = true;
        mRenderQueueID = queueID;
        // tell any existing regions
        for (auto & ri : mRegionMap)
        {
            ri.second->setRenderQueueGroup(queueID);
        }
    }
    //--------------------------------------------------------------------------
    uint8 StaticGeometry::getRenderQueueGroup(void) const
    {
        return mRenderQueueID;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::setVisibilityFlags(uint32 flags)
    {
        mVisibilityFlags = flags;
        for (RegionMap::const_iterator ri = mRegionMap.begin();
            ri != mRegionMap.end(); ++ri)
        {
            ri->second->setVisibilityFlags(flags);
        }
    }
    //--------------------------------------------------------------------------
    uint32 StaticGeometry::getVisibilityFlags() const
    {
        if(mRegionMap.empty())
            return MovableObject::getDefaultVisibilityFlags();

        RegionMap::const_iterator ri = mRegionMap.begin();
        return ri->second->getVisibilityFlags();
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::dump(const String& filename) const
    {
        std::ofstream of(filename.c_str());
        of << "Static Geometry Report for " << mName << std::endl;
        of << "-------------------------------------------------" << std::endl;
        of << "Number of queued submeshes: " << mQueuedSubMeshes.size() << std::endl;
        of << "Number of regions: " << mRegionMap.size() << std::endl;
        of << "Region dimensions: " << mRegionDimensions << std::endl;
        of << "Origin: " << mOrigin << std::endl;
        of << "Max distance: " << mUpperDistance << std::endl;
        of << "Casts shadows?: " << mCastShadows << std::endl;
        of << std::endl;
        for (auto ri : mRegionMap)
        {
            ri.second->dump(of);
        }
        of << "-------------------------------------------------" << std::endl;
    }
    //---------------------------------------------------------------------
    void StaticGeometry::visitRenderables(Renderable::Visitor* visitor,
        bool debugRenderables)
    {
        for (RegionMap::const_iterator ri = mRegionMap.begin();
            ri != mRegionMap.end(); ++ri)
        {
            ri->second->visitRenderables(visitor, debugRenderables);
        }

    }
    //--------------------------------------------------------------------------
    StaticGeometry::RegionIterator StaticGeometry::getRegionIterator(void)
    {
        return RegionIterator(mRegionMap.begin(), mRegionMap.end());
    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    StaticGeometry::Region::Region(StaticGeometry* parent, const String& name,
        SceneManager* mgr, uint32 regionID, const Vector3& centre)
        : MovableObject(name), mParent(parent),
        mRegionID(regionID), mCentre(centre), mBoundingRadius(0.0f),
        mCurrentLod(0), mLodStrategy(0), mCamera(0), mSquaredViewDepth(0)
    {
        mManager = mgr;
    }
    //--------------------------------------------------------------------------
    StaticGeometry::Region::~Region()
    {
        if (mParentNode)
        {
            mManager->destroySceneNode(static_cast<SceneNode*>(mParentNode));
            mParentNode = 0;
        }
        // delete
        for (auto & i : mLodBucketList)
        {
            OGRE_DELETE i;
        }
        mLodBucketList.clear();

        // no need to delete queued meshes, these are managed in StaticGeometry

    }
    //-----------------------------------------------------------------------
    void StaticGeometry::Region::_releaseManualHardwareResources()
    {
        for (auto & i : mLodBucketList)
        {
            clearShadowRenderableList(i->getShadowRenderableList());
        }
    }
    //-----------------------------------------------------------------------
    void StaticGeometry::Region::_restoreManualHardwareResources()
    {
        // shadow renderables are lazy initialized
    }
    //--------------------------------------------------------------------------
    uint32 StaticGeometry::Region::getTypeFlags(void) const
    {
        return SceneManager::STATICGEOMETRY_TYPE_MASK;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::Region::assign(QueuedSubMesh* qmesh)
    {
        mQueuedSubMeshes.push_back(qmesh);

        // Set/check LOD strategy
        const LodStrategy *lodStrategy = qmesh->submesh->parent->getLodStrategy();
        if (mLodStrategy == 0)
        {
            mLodStrategy = lodStrategy;

            // First LOD mandatory, and always from base LOD value
            mLodValues.push_back(mLodStrategy->getBaseValue());
        }
        else
        {
            if (mLodStrategy != lodStrategy)
                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Lod strategies do not match",
                    "StaticGeometry::Region::assign");
        }

        // update LOD values
        ushort lodLevels = qmesh->submesh->parent->getNumLodLevels();
        assert(qmesh->geometryLodList->size() == lodLevels);

        while(mLodValues.size() < lodLevels)
        {
            mLodValues.push_back(0.0f);
        }
        // Make sure LOD levels are max of all at the requested level
        for (ushort lod = 1; lod < lodLevels; ++lod)
        {
            const MeshLodUsage& meshLod =
                qmesh->submesh->parent->getLodLevel(lod);
            mLodValues[lod] = std::max(mLodValues[lod],
                meshLod.value);
        }

        // update bounds
        // Transform world bounds relative to our centre
        AxisAlignedBox localBounds(
            qmesh->worldBounds.getMinimum() - mCentre,
            qmesh->worldBounds.getMaximum() - mCentre);
        mAABB.merge(localBounds);
        mBoundingRadius = Math::boundingRadiusFromAABB(mAABB);

    }
    //--------------------------------------------------------------------------
    void StaticGeometry::Region::build(bool stencilShadows)
    {
        // Create a node
        mManager->getRootSceneNode()->createChildSceneNode(mCentre)->attachObject(this);
        // We need to create enough LOD buckets to deal with the highest LOD
        // we encountered in all the meshes queued
        for (ushort lod = 0; lod < mLodValues.size(); ++lod)
        {
            LODBucket* lodBucket =
                OGRE_NEW LODBucket(this, lod, mLodValues[lod]);
            mLodBucketList.push_back(lodBucket);
            // Now iterate over the meshes and assign to LODs
            // LOD bucket will pick the right LOD to use
            QueuedSubMeshList::iterator qi, qiend;
            qiend = mQueuedSubMeshes.end();
            for (qi = mQueuedSubMeshes.begin(); qi != qiend; ++qi)
            {
                lodBucket->assign(*qi, lod);
            }
            // now build
            lodBucket->build(stencilShadows);
        }

    }
    //--------------------------------------------------------------------------
    const String& StaticGeometry::Region::getMovableType(void) const
    {
        static String sType = "StaticGeometry";
        return sType;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::Region::_notifyCurrentCamera(Camera* cam)
    {
        // Set camera
        mCamera = cam;

        // Cache squared view depth for use by GeometryBucket
        mSquaredViewDepth = mParentNode->getSquaredViewDepth(cam->getLodCamera());

        // No LOD strategy set yet, skip (this indicates that there are no submeshes)
        if (mLodStrategy == 0)
            return;

        // Determine whether to still render
        mBeyondFarDistance = false;

        Real renderingDist = mParent->getRenderingDistance();
        if (renderingDist > 0 && cam->getUseRenderingDistance())
        {
            // Max distance to still render
            Real maxDist = renderingDist + mBoundingRadius;
            if (mSquaredViewDepth > Math::Sqr(maxDist))
            {
                mBeyondFarDistance = true;
                return;
            }
        }

        // Sanity check
        assert(!mLodValues.empty());

        // Calculate LOD value
        Real lodValue = mLodStrategy->getValue(this, cam);

        // Store LOD value for this strategy
        mLodValue = lodValue;

        // Get LOD index
        mCurrentLod = mLodStrategy->getIndex(lodValue, mLodValues);
    }
    //--------------------------------------------------------------------------
    const AxisAlignedBox& StaticGeometry::Region::getBoundingBox(void) const
    {
        return mAABB;
    }
    //--------------------------------------------------------------------------
    Real StaticGeometry::Region::getBoundingRadius(void) const
    {
        return mBoundingRadius;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::Region::_updateRenderQueue(RenderQueue* queue)
    {
        mLodBucketList[mCurrentLod]->addRenderables(queue, mRenderQueueID,
            mLodValue);
    }
    //---------------------------------------------------------------------
    void StaticGeometry::Region::visitRenderables(Renderable::Visitor* visitor,
        bool debugRenderables)
    {
        for (auto & i : mLodBucketList)
        {
            i->visitRenderables(visitor, debugRenderables);
        }

    }
    //--------------------------------------------------------------------------
    bool StaticGeometry::Region::isVisible(void) const
    {
        if(!mVisible || mBeyondFarDistance)
            return false;

        SceneManager* sm = Root::getSingleton()._getCurrentSceneManager();
        if (sm && !(mVisibilityFlags & sm->_getCombinedVisibilityMask()))
            return false;

        return true;
    }
    //--------------------------------------------------------------------------
    StaticGeometry::Region::LODIterator
    StaticGeometry::Region::getLODIterator(void)
    {
        return LODIterator(mLodBucketList.begin(), mLodBucketList.end());
    }
    //---------------------------------------------------------------------
    const ShadowRenderableList& StaticGeometry::Region::getShadowVolumeRenderableList(
        const Light* light, const HardwareIndexBufferPtr& indexBuffer, size_t& indexBufferUsedSize,
        float extrusionDistance, int flags)
    {
        // Calculate the object space light details
        Vector4 lightPos = light->getAs4DVector();
        Affine3 world2Obj = mParentNode->_getFullTransform().inverse();
        lightPos = world2Obj * lightPos;
        Matrix3 world2Obj3x3 = world2Obj.linear();
        extrusionDistance *= Math::Sqrt(std::min(std::min(world2Obj3x3.GetColumn(0).squaredLength(), world2Obj3x3.GetColumn(1).squaredLength()), world2Obj3x3.GetColumn(2).squaredLength()));

        // per-LOD shadow lists & edge data
        mLodBucketList[mCurrentLod]->updateShadowRenderables(lightPos, indexBuffer,
                                                             extrusionDistance, flags);

        EdgeData* edgeList = mLodBucketList[mCurrentLod]->getEdgeList();
        ShadowRenderableList& shadowRendList = mLodBucketList[mCurrentLod]->getShadowRenderableList();

        // Calc triangle light facing
        updateEdgeListLightFacing(edgeList, lightPos);

        // Generate indexes and update renderables
        generateShadowVolume(edgeList, indexBuffer, indexBufferUsedSize, light, shadowRendList, flags);

        return shadowRendList;

    }
    //--------------------------------------------------------------------------
    EdgeData* StaticGeometry::Region::getEdgeList(void)
    {
        return mLodBucketList[mCurrentLod]->getEdgeList();
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::Region::dump(std::ofstream& of) const
    {
        of << "Region " << mRegionID << std::endl;
        of << "--------------------------" << std::endl;
        of << "Centre: " << mCentre << std::endl;
        of << "Local AABB: " << mAABB << std::endl;
        of << "Bounding radius: " << mBoundingRadius << std::endl;
        of << "Number of LODs: " << mLodBucketList.size() << std::endl;

        for (auto i : mLodBucketList)
        {
            i->dump(of);
        }
        of << "--------------------------" << std::endl;
    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    StaticGeometry::LODBucket::LODBucket(Region* parent, unsigned short lod,
        Real lodValue)
        : mParent(parent), mLod(lod), mLodValue(lodValue), mEdgeList(0)
        , mVertexProgramInUse(false)
    {
    }
    //--------------------------------------------------------------------------
    StaticGeometry::LODBucket::~LODBucket()
    {
        OGRE_DELETE mEdgeList;
        ShadowCaster::clearShadowRenderableList(mShadowRenderables);
        // delete
        for (auto & i : mMaterialBucketMap)
        {
            OGRE_DELETE i.second;
        }
        mMaterialBucketMap.clear();
        for(auto & qi : mQueuedGeometryList)
        {
            OGRE_DELETE qi;
        }
        mQueuedGeometryList.clear();

        // no need to delete queued meshes, these are managed in StaticGeometry
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::LODBucket::assign(QueuedSubMesh* qmesh, ushort atLod)
    {
        QueuedGeometry* q = OGRE_NEW QueuedGeometry();
        mQueuedGeometryList.push_back(q);
        q->position = qmesh->position;
        q->orientation = qmesh->orientation;
        q->scale = qmesh->scale;
        if (qmesh->geometryLodList->size() > atLod)
        {
            // This submesh has enough lods, use the right one
            q->geometry = &(*qmesh->geometryLodList)[atLod];
        }
        else
        {
            // Not enough lods, use the lowest one we have
            q->geometry =
                &(*qmesh->geometryLodList)[qmesh->geometryLodList->size() - 1];
        }
        // Locate a material bucket
        MaterialBucket* mbucket = 0;
        MaterialBucketMap::iterator m =
            mMaterialBucketMap.find(qmesh->material->getName());
        if (m != mMaterialBucketMap.end())
        {
            mbucket = m->second;
        }
        else
        {
            mbucket = OGRE_NEW MaterialBucket(this, qmesh->material);
            mMaterialBucketMap[qmesh->material->getName()] = mbucket;
        }
        mbucket->assign(q);
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::LODBucket::build(bool stencilShadows)
    {

        EdgeListBuilder eb;
        size_t vertexSet = 0;

        // Just pass this on to child buckets
        for (auto & i : mMaterialBucketMap)
        {
            MaterialBucket* mat = i.second;

            mat->build(stencilShadows);

            if (stencilShadows)
            {
                // Check if we have vertex programs here
                Technique* t = mat->getMaterial()->getBestTechnique();
                if (t)
                {
                    Pass* p = t->getPass(0);
                    if (p)
                    {
                        if (p->hasVertexProgram())
                        {
                            mVertexProgramInUse = true;
                        }
                    }
                }

                for (GeometryBucket* geom : mat->getGeometryList())
                {
                    // Check we're dealing with 16-bit indexes here
                    // Since stencil shadows can only deal with 16-bit
                    // More than that and stencil is probably too CPU-heavy
                    // in any case
                    assert(geom->getIndexData()->indexBuffer->getType()
                        == HardwareIndexBuffer::IT_16BIT &&
                        "Only 16-bit indexes allowed when using stencil shadows");
                    eb.addVertexData(geom->getVertexData());
                    eb.addIndexData(geom->getIndexData(), vertexSet++);
                }

            }
        }

        if (stencilShadows)
        {
            mEdgeList = eb.build();
        }
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::LODBucket::addRenderables(RenderQueue* queue,
        uint8 group, Real lodValue)
    {
        // Just pass this on to child buckets
        MaterialBucketMap::iterator i, iend;
        iend =  mMaterialBucketMap.end();
        for (i = mMaterialBucketMap.begin(); i != iend; ++i)
        {
            i->second->addRenderables(queue, group, lodValue);
        }
    }
    //--------------------------------------------------------------------------
    StaticGeometry::LODBucket::MaterialIterator
    StaticGeometry::LODBucket::getMaterialIterator(void)
    {
        return MaterialIterator(
            mMaterialBucketMap.begin(), mMaterialBucketMap.end());
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::LODBucket::dump(std::ofstream& of) const
    {
        of << "LOD Bucket " << mLod << std::endl;
        of << "------------------" << std::endl;
        of << "LOD Value: " << mLodValue << std::endl;
        of << "Number of Materials: " << mMaterialBucketMap.size() << std::endl;
        for (const auto & i : mMaterialBucketMap)
        {
            i.second->dump(of);
        }
        of << "------------------" << std::endl;

    }
    //---------------------------------------------------------------------
    void StaticGeometry::LODBucket::visitRenderables(Renderable::Visitor* visitor,
        bool debugRenderables)
    {
        for (MaterialBucketMap::const_iterator i = mMaterialBucketMap.begin();
            i != mMaterialBucketMap.end(); ++i)
        {
            i->second->visitRenderables(visitor, debugRenderables);
        }

    }
    //---------------------------------------------------------------------
    void StaticGeometry::LODBucket::updateShadowRenderables(const Vector4& lightPos,
                                                            const HardwareIndexBufferPtr& indexBuffer,
                                                            Real extrusionDistance, int flags)
    {
        assert(indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT &&
               "Only 16-bit indexes supported for now");

        // We need to search the edge list for silhouette edges
        OgreAssert(mEdgeList, "You enabled stencil shadows after the build process!");

        // Init shadow renderable list if required
        bool init = mShadowRenderables.empty();
        bool extrude = flags & SRF_EXTRUDE_IN_SOFTWARE;

        EdgeData::EdgeGroupList::iterator egi;
        ShadowCaster::ShadowRenderableList::iterator si, siend;
        if (init)
            mShadowRenderables.resize(mEdgeList->edgeGroups.size());

        siend = mShadowRenderables.end();
        egi = mEdgeList->edgeGroups.begin();
        for (si = mShadowRenderables.begin(); si != siend; ++si, ++egi)
        {
            if (init)
            {
                // Create a new renderable, create a separate light cap if
                // we're using a vertex program (either for this model, or
                // for extruding the shadow volume) since otherwise we can
                // get depth-fighting on the light cap

                *si = OGRE_NEW ShadowRenderable(mParent, indexBuffer, egi->vertexData,
                                                mVertexProgramInUse || !extrude);
            }
            // Extrude vertices in software if required
            if (extrude)
            {
                mParent->extrudeVertices((*si)->getPositionBuffer(), egi->vertexData->vertexCount, lightPos,
                                         extrusionDistance);
            }
        }
    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    StaticGeometry::MaterialBucket::MaterialBucket(LODBucket* parent,
        const MaterialPtr& material)
        : mParent(parent)
        , mMaterial(material)
        , mTechnique(0)
    {
    }
    //--------------------------------------------------------------------------
    StaticGeometry::MaterialBucket::~MaterialBucket()
    {
        // delete
        for (auto & i : mGeometryBucketList)
        {
            OGRE_DELETE i;
        }
        mGeometryBucketList.clear();

        // no need to delete queued meshes, these are managed in StaticGeometry
    }
    //--------------------------------------------------------------------------
    static uint32 getHash(StaticGeometry::SubMeshLodGeometryLink* geom)
    {
        // Formulate an identifying string for the geometry format
        // Must take into account the vertex declaration and the index type
        // Format is:
        // Index type
        // Vertex element (repeating)
        //   source
        //   semantic
        //   type
        uint32 hash = HashCombine(0, geom->indexData->indexBuffer->getType());
        const auto& elemList = geom->vertexData->vertexDeclaration->getElements();
        for (const VertexElement& elem : elemList)
        {
            hash = HashCombine(hash, elem.getSource());
            hash = HashCombine(hash, elem.getSemantic());
            hash = HashCombine(hash, elem.getType());
        }

        return hash;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::MaterialBucket::assign(QueuedGeometry* qgeom)
    {
        // Look up any current geometry
        uint32 hash = getHash(qgeom->geometry);
        CurrentGeometryMap::iterator gi = mCurrentGeometryMap.find(hash);
        bool newBucket = true;
        if (gi != mCurrentGeometryMap.end())
        {
            // Found existing geometry, try to assign
            newBucket = !gi->second->assign(qgeom);
            // Note that this bucket will be replaced as the 'current'
            // for this hash string below since it's out of space
        }
        // Do we need to create a new one?
        if (newBucket)
        {
            GeometryBucket* gbucket =
                OGRE_NEW GeometryBucket(this, qgeom->geometry->vertexData, qgeom->geometry->indexData);
            // Add to main list
            mGeometryBucketList.push_back(gbucket);
            // Also index in 'current' list
            mCurrentGeometryMap[hash] = gbucket;
            if (!gbucket->assign(qgeom))
            {
                OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
                    "Somehow we couldn't fit the requested geometry even in a "
                    "brand new GeometryBucket!! Must be a bug, please report.",
                    "StaticGeometry::MaterialBucket::assign");
            }
        }
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::MaterialBucket::build(bool stencilShadows)
    {
        mTechnique = 0;
        mMaterial->load();
        // tell the geometry buckets to build
        for (auto gb : mGeometryBucketList)
        {
            gb->build(stencilShadows);
        }
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::MaterialBucket::addRenderables(RenderQueue* queue,
        uint8 group, Real lodValue)
    {
        // Get region
        Region *region = mParent->getParent();

        // Get material LOD strategy
        const LodStrategy *materialLodStrategy = mMaterial->getLodStrategy();

        // If material strategy doesn't match, recompute LOD value with correct strategy
        if (materialLodStrategy != region->mLodStrategy)
            lodValue = materialLodStrategy->getValue(region, region->mCamera);

        // Determine the current material technique
        mTechnique = mMaterial->getBestTechnique(
            mMaterial->getLodIndex(lodValue));
        GeometryBucketList::iterator i, iend;
        iend =  mGeometryBucketList.end();
        for (i = mGeometryBucketList.begin(); i != iend; ++i)
        {
            queue->addRenderable(*i, group);
        }

    }
    void StaticGeometry::MaterialBucket::_setMaterial(const MaterialPtr& material)
    {
        OgreAssert(material, "NULL pointer");
        mMaterial = material;
        mMaterial->load();
    }
    //--------------------------------------------------------------------------
    StaticGeometry::MaterialBucket::GeometryIterator
    StaticGeometry::MaterialBucket::getGeometryIterator(void)
    {
        return GeometryIterator(
            mGeometryBucketList.begin(), mGeometryBucketList.end());
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::MaterialBucket::dump(std::ofstream& of) const
    {
        of << "Material Bucket " << getMaterialName() << std::endl;
        of << "--------------------------------------------------" << std::endl;
        of << "Geometry buckets: " << mGeometryBucketList.size() << std::endl;
        for (auto i : mGeometryBucketList)
        {
            i->dump(of);
        }
        of << "--------------------------------------------------" << std::endl;

    }
    //---------------------------------------------------------------------
    void StaticGeometry::MaterialBucket::visitRenderables(Renderable::Visitor* visitor,
        bool debugRenderables)
    {
        for (auto i : mGeometryBucketList)
        {
            visitor->visit(i, mParent->getLod(), false);
        }

    }
    //--------------------------------------------------------------------------
    //--------------------------------------------------------------------------
    StaticGeometry::GeometryBucket::GeometryBucket(MaterialBucket* parent, const VertexData* vData,
                                                   const IndexData* iData)
        : Renderable(), mParent(parent)
    {
        // Clone the structure from the example
        mVertexData = vData->clone(false);
        mIndexData = iData->clone(false);
        mVertexData->vertexCount = 0;
        mVertexData->vertexStart = 0;
        mIndexData->indexCount = 0;
        mIndexData->indexStart = 0;
        // Derive the max vertices
        if (iData->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT)
        {
            mMaxVertexIndex = 0xFFFFFFFF;
        }
        else
        {
            mMaxVertexIndex = 0xFFFF;
        }

        // Check to see if we have blend indices / blend weights
        // remove them if so, they can try to blend non-existent bones!
        const VertexElement* blendIndices =
            mVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_INDICES);
        const VertexElement* blendWeights =
            mVertexData->vertexDeclaration->findElementBySemantic(VES_BLEND_WEIGHTS);
        if (blendIndices && blendWeights)
        {
            assert(blendIndices->getSource() == blendWeights->getSource()
                && "Blend indices and weights should be in the same buffer");
            // Get the source
            ushort source = blendIndices->getSource();
            assert(blendIndices->getSize() + blendWeights->getSize() ==
                mVertexData->vertexBufferBinding->getBuffer(source)->getVertexSize()
                && "Blend indices and blend buffers should have buffer to themselves!");
            // Unset the buffer
            mVertexData->vertexBufferBinding->unsetBinding(source);
            // Remove the elements
            mVertexData->vertexDeclaration->removeElement(VES_BLEND_INDICES);
            mVertexData->vertexDeclaration->removeElement(VES_BLEND_WEIGHTS);
            // Close gaps in bindings for effective and safely
            mVertexData->closeGapsInBindings();
        }

    }
    //--------------------------------------------------------------------------
    StaticGeometry::GeometryBucket::~GeometryBucket()
    {
        OGRE_DELETE mVertexData;
        OGRE_DELETE mIndexData;
    }
    //--------------------------------------------------------------------------
    const MaterialPtr& StaticGeometry::GeometryBucket::getMaterial(void) const
    {
        return mParent->getMaterial();
    }
    //--------------------------------------------------------------------------
    Technique* StaticGeometry::GeometryBucket::getTechnique(void) const
    {
        return mParent->getCurrentTechnique();
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::GeometryBucket::getRenderOperation(RenderOperation& op)
    {
        op.indexData = mIndexData;
        op.operationType = RenderOperation::OT_TRIANGLE_LIST;
        op.srcRenderable = this;
        op.useIndexes = true;
        op.vertexData = mVertexData;
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::GeometryBucket::getWorldTransforms(Matrix4* xform) const
    {
        // Should be the identity transform, but lets allow transformation of the
        // nodes the regions are attached to for kicks
        *xform = mParent->getParent()->getParent()->_getParentNodeFullTransform();
    }
    //--------------------------------------------------------------------------
    Real StaticGeometry::GeometryBucket::getSquaredViewDepth(const Camera* cam) const
    {
        const Region *region = mParent->getParent()->getParent();
        if (cam == region->mCamera)
            return region->mSquaredViewDepth;
        else
            return region->getParentNode()->getSquaredViewDepth(cam->getLodCamera());
    }
    //--------------------------------------------------------------------------
    const LightList& StaticGeometry::GeometryBucket::getLights(void) const
    {
        return mParent->getParent()->getParent()->queryLights();
    }
    //--------------------------------------------------------------------------
    bool StaticGeometry::GeometryBucket::getCastsShadows(void) const
    {
        return mParent->getParent()->getParent()->getCastShadows();
    }
    //--------------------------------------------------------------------------
    bool StaticGeometry::GeometryBucket::assign(QueuedGeometry* qgeom)
    {
        // Do we have enough space?
        // -2 first to avoid overflow (-1 to adjust count to index, -1 to ensure
        // no overflow at 32 bits and use >= instead of >)
        if ((mVertexData->vertexCount - 2 + qgeom->geometry->vertexData->vertexCount)
            >= mMaxVertexIndex)
        {
            return false;
        }

        mQueuedGeometry.push_back(qgeom);
        mVertexData->vertexCount += qgeom->geometry->vertexData->vertexCount;
        mIndexData->indexCount += qgeom->geometry->indexData->indexCount;

        return true;
    }
    //--------------------------------------------------------------------------
    template<typename T>
    static void copyIndexes(const T* src, T* dst, size_t count, uint32 indexOffset)
    {
        if (indexOffset == 0)
        {
            memcpy(dst, src, sizeof(T) * count);
        }
        else
        {
            while(count--)
            {
                *dst++ = static_cast<T>(*src++ + indexOffset);
            }
        }
    }
    void StaticGeometry::GeometryBucket::build(bool stencilShadows)
    {
        // Need to double the vertex count for the position buffer
        // if we're doing stencil shadows
        OgreAssert(!stencilShadows || mVertexData->vertexCount * 2 <= mMaxVertexIndex,
                   "Index range exceeded when using stencil shadows, consider reducing your region size or "
                   "reducing poly count");

        // Ok, here's where we transfer the vertices and indexes to the shared
        // buffers
        // Shortcuts
        VertexDeclaration* dcl = mVertexData->vertexDeclaration;
        VertexBufferBinding* binds = mVertexData->vertexBufferBinding;

        // create index buffer, and lock
        auto indexType = mIndexData->indexBuffer->getType();
        mIndexData->indexBuffer = HardwareBufferManager::getSingleton()
            .createIndexBuffer(indexType, mIndexData->indexCount,
                HardwareBuffer::HBU_STATIC_WRITE_ONLY);
        HardwareBufferLockGuard dstIndexLock(mIndexData->indexBuffer, HardwareBuffer::HBL_DISCARD);
        uint32* p32Dest = static_cast<uint32*>(dstIndexLock.pData);
        uint16* p16Dest = static_cast<uint16*>(dstIndexLock.pData);
        // create all vertex buffers, and lock
        ushort b;

        std::vector<uchar*> destBufferLocks;
        std::vector<VertexDeclaration::VertexElementList> bufferElements;
        for (b = 0; b < binds->getBufferCount(); ++b)
        {
            HardwareVertexBufferSharedPtr vbuf =
                HardwareBufferManager::getSingleton().createVertexBuffer(
                    dcl->getVertexSize(b),
                    mVertexData->vertexCount,
                    HardwareBuffer::HBU_STATIC_WRITE_ONLY);
            binds->setBinding(b, vbuf);
            uchar* pLock = static_cast<uchar*>(
                vbuf->lock(HardwareBuffer::HBL_DISCARD));
            destBufferLocks.push_back(pLock);
            // Pre-cache vertex elements per buffer
            bufferElements.push_back(dcl->findElementsBySource(b));
        }

        // Iterate over the geometry items
        uint32 indexOffset = 0;
        QueuedGeometryList::iterator gi, giend;
        giend = mQueuedGeometry.end();
        Vector3 regionCentre = mParent->getParent()->getParent()->getCentre();
        for (gi = mQueuedGeometry.begin(); gi != giend; ++gi)
        {
            QueuedGeometry* geom = *gi;
            // Copy indexes across with offset
            IndexData* srcIdxData = geom->geometry->indexData;
            HardwareBufferLockGuard srcIdxLock(srcIdxData->indexBuffer,
                                               srcIdxData->indexStart * srcIdxData->indexBuffer->getIndexSize(),
                                               srcIdxData->indexCount * srcIdxData->indexBuffer->getIndexSize(),
                                               HardwareBuffer::HBL_READ_ONLY);
            if (indexType == HardwareIndexBuffer::IT_32BIT)
            {
                uint32* pSrc = static_cast<uint32*>(srcIdxLock.pData);
                copyIndexes(pSrc, p32Dest, srcIdxData->indexCount, indexOffset);
                p32Dest += srcIdxData->indexCount;
            }
            else
            {
                // Lock source indexes
                uint16* pSrc = static_cast<uint16*>(srcIdxLock.pData);
                copyIndexes(pSrc, p16Dest, srcIdxData->indexCount, indexOffset);
                p16Dest += srcIdxData->indexCount;
            }
            srcIdxLock.unlock();

            // Now deal with vertex buffers
            // we can rely on buffer counts / formats being the same
            VertexData* srcVData = geom->geometry->vertexData;
            VertexBufferBinding* srcBinds = srcVData->vertexBufferBinding;
            for (b = 0; b < binds->getBufferCount(); ++b)
            {
                // lock source
                HardwareVertexBufferSharedPtr srcBuf = srcBinds->getBuffer(b);
                HardwareBufferLockGuard srcBufLock(srcBuf, HardwareBuffer::HBL_READ_ONLY);
                uchar* pSrcBase = static_cast<uchar*>(srcBufLock.pData);
                // Get buffer lock pointer, we'll update this later
                uchar* pDstBase = destBufferLocks[b];
                size_t bufInc = srcBuf->getVertexSize();

                // Iterate over vertices
                float *pSrcReal, *pDstReal;
                Vector3 tmp;
                for (size_t v = 0; v < srcVData->vertexCount; ++v)
                {
                    // Iterate over vertex elements
                    VertexDeclaration::VertexElementList& elems =
                        bufferElements[b];
                    VertexDeclaration::VertexElementList::iterator ei;
                    for (ei = elems.begin(); ei != elems.end(); ++ei)
                    {
                        VertexElement& elem = *ei;
                        elem.baseVertexPointerToElement(pSrcBase, &pSrcReal);
                        elem.baseVertexPointerToElement(pDstBase, &pDstReal);
                        switch (elem.getSemantic())
                        {
                        case VES_POSITION:
                            tmp.x = *pSrcReal++;
                            tmp.y = *pSrcReal++;
                            tmp.z = *pSrcReal++;
                            // transform
                            tmp = (geom->orientation * (tmp * geom->scale)) +
                                geom->position;
                            // Adjust for region centre
                            tmp -= regionCentre;
                            *pDstReal++ = tmp.x;
                            *pDstReal++ = tmp.y;
                            *pDstReal++ = tmp.z;
                            break;
                        case VES_NORMAL:
                        case VES_TANGENT:
                        case VES_BINORMAL:
                            tmp.x = *pSrcReal++;
                            tmp.y = *pSrcReal++;
                            tmp.z = *pSrcReal++;
                            // scale (invert)
                            tmp = tmp / geom->scale;
                            tmp.normalise();
                            // rotation
                            tmp = geom->orientation * tmp;
                            *pDstReal++ = tmp.x;
                            *pDstReal++ = tmp.y;
                            *pDstReal++ = tmp.z;
                            // copy parity for tangent.
                            if (elem.getType() == Ogre::VET_FLOAT4)
                                *pDstReal = *pSrcReal;
                            break;
                        default:
                            // just raw copy
                            memcpy(pDstReal, pSrcReal,
                                    VertexElement::getTypeSize(elem.getType()));
                            break;
                        };

                    }

                    // Increment both pointers
                    pDstBase += bufInc;
                    pSrcBase += bufInc;

                }

                // Update pointer
                destBufferLocks[b] = pDstBase;
            }

            indexOffset += geom->geometry->vertexData->vertexCount;
        }

        // Unlock everything
        dstIndexLock.unlock();
        for (b = 0; b < binds->getBufferCount(); ++b)
        {
            binds->getBuffer(b)->unlock();
        }

        if (stencilShadows)
        {
            mVertexData->prepareForShadowVolume();
        }
    }
    //--------------------------------------------------------------------------
    void StaticGeometry::GeometryBucket::dump(std::ofstream& of) const
    {
        of << "Geometry Bucket" << std::endl;
        of << "---------------" << std::endl;
        of << "Geometry items: " << mQueuedGeometry.size() << std::endl;
        of << "Vertex count: " << mVertexData->vertexCount << std::endl;
        of << "Index count: " << mIndexData->indexCount << std::endl;
        of << "---------------" << std::endl;

    }
    //--------------------------------------------------------------------------
    String StaticGeometryFactory::FACTORY_TYPE_NAME = "StaticGeometry";
}


text:
the Indestructibles
Who are the Indestructibles?
New Indestructibles novella free now til midnight, December 31!
What happens when the Indestructibles get trapped in a cursed board game reminiscent of Dungeons and Dragons? We find out who the bard is, first of all. Plus all sorts of random geeky fun. Get Roll for Initiative free on Amazon Kindle from now til December 31 at midnight!
The Indestructibles, a team of super-powered teenagers, have been busy. Whether saving the world or traveling to alternate timelines, there isn't much downtime. But when small respite from the action leads them to start bickering with each other, Entropy Emily, the gravity-controlling resident instigator, decides the best thing her teammates can do is have a game night, and leads them—not entirely voluntarily—into a game that seems to be a cross between a classic pen and paper role playing game and dungeon-delving board game. Unfortunately, she pilfered the game from their mentor, the magician Doc Silence—and it turns out the game just might be cursed.
One roll of the dice and the team ends up trapped in a medieval setting, without their powers, and armed with swords, bows, and a little bit of magic. They'll have to use their wits to figure out the rules of this cursed game, and convincingly play the roles they've been assigned, to find their way home.
When you're a hero, even game night turns into a life and death adventure. Will the heroes find their way home? Or will they remain trapped forever as paladins and bards, rangers and wizards?
paper writing service review link
This one is best and interesting novel. I have read many blogs because it can release the tension in our mind. But its not good for teen age peoples and they need to the education for success.
Matthew Phillion is the writer of "The Indestructibles," part-time actor, occasional filmmaker. Currently on the lam in Salem with his trusty dog, Watson.

redpajama_set_name: RedPajamaCommonCrawl


text:
Without Stewart…The News (And Laughter) Must Go On
Yes, Comedy Central is still a fine station. "The Daily Show…" is also okay. Trevor Noah, the new host of the show, is trying his absolute best. But, let's face it, asking Noah or anyone else to do the job is like asking that ridiculous comedian to truly BE Colonel Sanders and understand the concept of why Kentucky Fried Chicken seriously rules!
It is heartbreaking not seeing Jon Stewart's requisite "quilt of looks" as he tells the rest of us the dumbest – yet truest news each and every day. His laughter as he brought the headlines to our attention is greatly missed. But…it is a fact that when it comes to news, the dumbest of the dumb must still be shared. (Just imagine Stewart's grin while reading)
For those who are unaware, the large state of New Mexico has had some interesting news in the last couple of weeks. First, a van was transporting five inmates between jails that are 170 miles apart. The story is a bit fuzzy but, supposedly, the police stopped to get gas or something at a mini-mart, and when they reached their destination 170 miles later they found out that only three inmates were still on board. Thankfully, the bad guys got re-caught, but…did the cops include themselves when they did the count, or is five just a really high number in the dry, lonely desert? Hmmm.
One other New Mexico tidbit comes from Roswell AND has to do with a UFO. The silver decorative UFO that was attached to the side of the Roswell UFO Museum was taken down for repairs after a strange and hardly-ever-seen snowstorm hit the town and damaged it this past winter. The UFO was in the back of the building under an awning being repaired when three men pulled up, lifted the UFO, put it in the back of their truck and drove away. The UFO was found shattered into pieces along a main highway the next day. No arrests have been made. The perpetrators and their crime was caught by a trusty security camera. Is it important to say that the actual police department is just a couple of feet away across the road? Probably not.
More fun headlines are out there. News comes in the form of a Mascot party held in University City at the Palestra; the University of Pennsylvania. Really furry friends, this includes the NBC10 (local) Peacock, broke out their dance moves to break a Guinness World Record. Great video is available and money was raised for childhood cancer.
In Suffolk County, New York, Girl Scouts proved to one and all that they were the "smart cookies" when it came to selling those thin mints that everyone loves. They took a picture of Leonardo DiCaprio who was eating Trefoils at the Oscar ceremony and made it into a poster to attract more customers. Talk about star power, these scouts proved they will one day leave their badges behind and be entrepreneurs on "Shark Tank."
In Arizona, there is a sign on the road from Nogales to Tucson that lists miles to go in kilometers, just to make all those students (including me) who despised the metric system in high school must do math while driving. When you think about signs that should be changed, however, the worst are those found all across the country where someone had no idea how to spell the word: SCHOOL. Look them up. Everything from SKOOL to SCOOL to SCHOL has been painted on America's roads…which is actually far more scary than funny.
In North Carolina a man was arrested for not returning a VHS tape that he'd obviously really loved, considering he'd had it for the past fourteen years. Police pulled his car over for a busted taillight and when the officers pulled up his license they found the warrant issued in 2002. The charge? Failure to return hired property. No movie is worth all this silliness.
And let's not forget a past headline featuring an alligator that was thrown through a drive-up window in Florida. Gator is fine, thankfully. But not only must you feel upset for the poor gator because he probably thought his loved ones had become part of the menu, but also because he had to be in a fast food joint smelling that crap instead of having a lovely jungle dinner with his female counterpart.
Television host Jon Stewart reacts during a taping of "The Daily Show with Jon Stewart," in New York, on Wednesday Nov. 30, 2011. (AP Photo/Brad Barket)
With all the tragedy in this world today, it is becoming clearer that Jon Stewart's ability and talent to make us all laugh was and is necessary. Humor has a way of making the brighter spots in life rise above the hideous crimes people are choosing to commit in this day and age. So, as the politics and painful news continues, here's some advice. Take a moment to laugh at least once a day. We all need it.
Filed Under: Lifestyle Tagged With: Arizona, Comedy, crazy news, Girl Scouts, headlines, Jon Stewart, laughter, New Mexico, North Carolina


There is no argument against the impact of strenuous firefighting and occupational exposures on firefighters. Firefighting is a dangerous profession that presents many risks that cannot be completely controlled.
However, firefighter health is not one of them.
Research has repeatedly shown the detrimental effects firefighting has on the human body, and the combination of heat stress and dehydration exacerbates this punishing impact on all of our body systems. Exposure to carcinogens remains a serious concern, and we cannot ignore the mental health and wellbeing of our personnel.
With all that we are up against, the research also clearly demonstrates the positive impact of annual medical evaluations, exercise, sensible nutrition, hydration, and rest and recovery. This is fact, not conjecture.
According to the National Fire Protection Association's (NFPA) most recent needs-assessment survey, only an embarrassingly low 27% of fire departments have even a basic health-and-wellness program in place. Often, the financial impact of health-and-wellness program implementation is cited as a barrier to improvement. Yet many studies have shown a positive return on investment (ROI) on these programs – many times in the first year of implementation.
Grant programs have consistently given priority to health-and-wellness initiatives, and there are more resources available than ever before – research-backed resources (many of them free) to help fire service organizations implement wellness programs.
There will always be obstacles to overcome, and they will vary from department to department. But one thing cannot be denied: Excuses will never reduce health-related line-of-duty deaths.
Consider an Oregon study that compared dollars spent on firefighter health and dollars spent on apparatus repair. As reported in The Fire Service Joint Labor Management Wellness-Fitness Initiative (PDF), a sample department allocated 70% to apparatus maintenance and 30% to repair versus 3% on firefighter health maintenance and 97% to repair. The average annual cost savings of $563,334 for departments that implemented WFI clearly suggests that health-and-wellness programs not only reduce the financial impact of injuries and illnesses, but also increase overall firefighter health, performance and longevity.
Many firefighter health-and-wellness advocates have turned their focus to individual firefighters to raise awareness and educate them on the need to get and stay fit for duty. While this approach is having a positive effect, the time is now for fire service leadership to prioritize firefighter health and wellness.
The research has been done. The data doesn't lie. Regardless of the obstacles, barriers, challenges and, yes, excuses, tangible action is what is needed to improve the overall wellness of our organizations.
Simply put, we should not be questioning whether we can afford firefighter health-and-wellness programs. We should be questioning whether we can afford to avoid them.
Dan Kerrigan, EFO, CFO, is the chief fire marshal and department fitness coordinator for the East Whiteland Township (Pennsylvania) Fire Department and a member of the Safety, Health & Survival Section's Board. He's been a member of the IAFC since 2013.
The theme for Safety Stand Down 2019 is "Reduce Your Exposure: It's Everybody's Responsibility," focusing on occupational cancer prevention. Agencies are asked to suspend all non-emergency activities during the week of June 16‑22 to focus on training related to this theme. Information, resources, training downloads and videos on the theme will be available at SafetyStandDown.org.
Again this year, the NFPA is hosting the Fire Service Safety Stand Down Quiz to increase awareness of first-responder health and wellness issues. Complete the quiz today and you will be automatically entered into a sweepstakes, and 200 randomly selected participants will win a specially designed commemorative Safety Stand Down challenge coin.


Jess is the co-presenter for Rereātea, Māori Television's online midday news bulletin, and an Online Reporter.
Jess first joined our news team in 2017 as an Assignment Editor for Te Kāea. She began her journalism career working as an Assignments Assistant and Online Reporter at TVNZ.
Jess was originally brought up in Whanganui but moved to Tāmaki Makaurau to study a Bachelor of Communications Degree majoring in journalism at AUT University. It was at university where Jess' passion for reporting Māori news stories grew. After graduating from her degree in 2014 she decided to pursue her passion to learn te reo Māori by studying a full time reo course at Te Wānanga Takiura in Auckland.
A Māori woman living on Australia's Gold Coast claims to have been unfairly 'bullied' out of her job due to her moko kauae.
Dr Rangi Matamua has weighed in on the first ever image of a black hole, saying it reminds him of Whiro, the god of darkness in Māori mythology.
On Wednesday police received reports of a possible gunshot being fired in Glen Eden and local schools were placed in lock-down.


Named after the Greek Mother goddess, Gaia, the creator and earth mother who protects and rules over all. Inspired by our favorite balancing shape, a triangle, this collection is meant to keep and protect its wearer. Emerald eternity band with engraved triangle detailing around each stone.


text:
Q: Customizing a Progress Bar appearance in Xamarin.Forms I used Drawable to customize the rendering of the ProgressBar in Android as answered to this question but the solution is not working with iOS.
Below is how it renders in Android.

Below is how it renders in iOS

Below is the code for my iOS CustomRenderer
[assembly: ExportRenderer(typeof(CustomProgressbar), typeof(CustomProgressBarRenderer))]
namespace Demo.iOS.Renderers
{
public class CustomProgressBarRenderer : ProgressBarRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
    {
        try
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
               Control.ProgressTintColor = Color.FromHex("#ff0000").ToUIColor();
               Control.TrackTintColor = Color.FromHex("#3489cc").ToUIColor();
             }
        }
        catch (Exception ex)
        {

        }
    }

    public override void LayoutSubviews()
    {
        base.LayoutSubviews();
        var X = 1.0f;
        var Y = 15.0f;
        CGAffineTransform _transform = CGAffineTransform.MakeScale(X, Y);
        this.Transform = _transform;
        this.ClipsToBounds = true;
        this.Layer.MasksToBounds = true;
        this.Layer.CornerRadius = 5;
    }
}

}
How do I accomplish this?

A: According to @SushiHangover's answer, we can make a ViewRenderer to achieve your effect.
Firstly, create our own ProgressView, make sure it is inherited from ContentView. also add a BindableProperty to present the value:
public partial class ProgressView : ContentView
{
    public double Progress
    {
        set { SetValue(ProgressProperty, value); }
        get { return (double)GetValue(ProgressProperty); }
    }

    public readonly static BindableProperty ProgressProperty = BindableProperty.Create("Progress", typeof(double), typeof(ProgressView), 0.0);

    ...
}

Then, We can make the Custom renderer like:
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
    base.OnElementChanged(e);

    //You can refer to @SushiHangover's method for detail's code, here I use the same name.
    Setup();
    Complete = ((ProgressView)Element).Progress;
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);

    if (e.PropertyName == "Progress")
    {
        Complete = ((ProgressView)Element).Progress;
    }
}

Because this is in renderer, we should refresh our label's frame:
public override void Draw(CGRect rect)
{
    base.Draw(rect);
    ...
    label.Frame = Bounds;
}

At last we can use it on Forms like:
<local:ProgressView x:Name="MyProgress" HeightRequest="50"/>

redpajama_set_name: RedPajamaStackExchange


text:
Siddhesh Joshi
I enjoy creating and spreading knowledgeable content for everyone around the world and try my best not to leave even the smallest of mistakes go unnoticed.
Aaron Gwin
Full name Aaron Holmes Gwin
Name Aaron Gwin
Disciplines Mountain bike racing
Role Biker
Born December 24, 1987 (age 28) (1987-12-24)
Rider type Downhill mountain biking
First look aaron gwin s new world cup dh bike
Aaron Holmes Gwin is an American professional downhill mountain biker from Morongo Valley, California. He is a five-time World Cup overall champion.
Aaron gwin wins mtb world cup title in val di sole
Race career
Aaron Gwin started his cycling career off early racing BMX from age 4, by age 8 Gwin was racing all the nationals. At age 12 Gwin moved on to motocross and raced up until he was 17 when he quit due to constant injuries. In 2008 at age 20 Gwin was loaned a bike by fellow professional downhill racer and good friend Cody Warren and was encouraged to compete in a race. In his first race in the Fontana Winter Series he got 3rd and began racing from then on, quickly signing with Yeti Cycles. He became well-known for breaking into the international World Cup scene after only 8 months of riding downhill.
Many saw Gwin as the savior for US downhilling on the international scene since his 10th-place finish at the Mont Sainte-Anne World Cup in 2008 was the first American top 10 finish since 2004. He has since won two United States National Championships for Downhill, in 2009 and in 2010. Both were held in SolVista, Colorado.
In 2011 and 2012 Gwin dominated the UCI World Cup downhill series. Gwin won 5 of 7 World Cup races in 2011, and finished 13th at the UCI World Championships in Champery. In 2012, Gwin once again dominated the circuit, taking first place in 4 of 6 World Cup races. He finished in a disappointing 128th at World Championships in Leogang.
The year 2013 was a disappointing one for Gwin with unexpected poor results culminating in an accident involving a tree at the World Championships
Returning with a vengeance his 2014 and 2015 campaigns continued to provide International respect for Gwin, Although with only one 1st in the World Cup along with the USA National Championships in 2014. His racing showed recovery from his 2013 setbacks but whose runs remain notoriously plagued with, what some think, the worst luck on the circuits, such as this run in Leogang at the UCI MTB World Cup in 2014 where his tyre flatted as he left the start then came off or this now famous ride from 2015 again at Leogang UCI MTB World Cup where his chain broke on the start line and he put in a text-book perfect pedal stoke-less ride holding his speed to win the race.
Much of Gwin's style and skill come from his motocross days, and his whips and jumping technique show it. He also gained a lot of flow from racing BMX for so many years, bringing him success in Dual Slalom and Mountain Cross. Aaron left the Yeti team to join the Trek World Racing Team in November 2010 after relocating to Temecula, CA. During his time at Trek World Racing, he won two World Cup Overall titles, in 2011 and 2012. On January 7, 2013, it was announced that Aaron would be riding for Specialized Racing, replacing Sam Hill on the team. His team mate at Specialized is Troy Brosnan.
After a successful season with Specialized, Gwin announced that he would be leaving the big brand to ride for up and coming brand YT Industries for the 2016 season.
10th place, 2008 UCI Mountain Bike World Cup #4 Mont Sainte-Anne, Quebec Canada34th place, 2008 UCI Mountain Bike World Cup #5 Bromont, Quebec, Canada8th place, 2008 UCI Mountain Bike World Cup #8 Schladming, Austria
17th place, 2009 UCI Mountain Bike World Cup #1 Pietermaritzburg, South Africa14th place, 2009 UCI Mountain Bike World Cup #2 La Bresse France16th place, 2009 UCI Mountain Bike World Cup #3 Vallnord Andorra9th place, 2009 UCI Mountain Bike World Cup #4 Fort William, Scotland14th place, 2009 UCI Mountain Bike World Cup #5 Maribor Slovenia1st place, USAC National Downhill Championships, Sol Vista Colorado United States3rd place, 2009 UCI Mountain Bike World Cup #6 Mont Sainte-Anne, Quebec, Canada5th place, 2009 UCI Mountain Bike World Cup #7 Bromont, Quebec, Canada21st place, 2009 UCI Mountain Bike World Championships Stromlo, Canberra, Australia4th place, 2009 UCI Mountain Bike World Cup #8 Schladming, Austria
7th place, 2010 UCI Mountain Bike World Cup #1 Maribor, Slovenia4th place, 2010 UCI Mountain Bike World Cup #2 Fort William, Scotland3rd place, 2010 UCI Mountain Bike World Cup #3 Leogang, Austria1st place, USAC National Downhill Championships, Sol Vista Colorado United States8th place, 2010 UCI Mountain Bike World Cup #4 Champerey, [France]14th place, 2010 UCI Mountain Bike World Cup #5 Val di Sole, [Italy]4th place, 2010 UCI Mountain Bike World Cup #6 Windham, New York State, United States4th place, 2010 UCI Mountain Bike World Championships Mont Sainte-Anne, Quebec, Canada
1st place overall for the 2011 UCI Mountain Bike World Cup, the first American to ever win a World Cup Overall Title and the first man to win 5 World Cup DH races in one season:1st place, 2011 UCI Mountain Bike World Cup #1 Pietermaritzburg, South Africa5th place, 2011 UCI Mountain Bike World Cup #2 Fort William, Scotland1st place, 2011 UCI Mountain Bike World Cup #3 Leogang, Austria1st place, 2011 UCI Mountain Bike World Cup #4 Mont Sainte-Anne, Quebec1st place, 2011 UCI Mountain Bike World Cup #5 Windham, New York, USA3rd place, 2011 UCI Mountain Bike World Cup #6 La Bresse, FRA1st place, 2011 UCI Mountain Bike World Cup #7 Val di Sole, ITA
1st place overall for the 2012 UCI Mountain Bike World Cup2nd place, 2012 UCI Mountain Bike World Cup #1 Pietermaritzburg, South Africa1st place, 2012 UCI Mountain Bike World Cup #2 Val Di Sole, Italy1st place, 2012 UCI Mountain Bike World Cup #3 Fort William, Scotland1st place, 2012 UCI Mountain Bike World Cup #4 Mont Saint Anne, Canada1st place, 2012 UCI Mountain Bike World Cup #5 Windham, United States5th place, 2012 UCI Mountain Bike World Cup #6 Val D'Isere, France (After this race he clinched his 2nd UCI Mountain Bike World Cup championship)83rd place, Leogang, UCI MTB World Championships 2012 AustriaDNF, 2012 UCI Mountain Bike World Cup #7 Hafjell Bike Park, Norway
20th place, 2013 UCI Mountain Bike World Cup #1 Fort William,6th place, 2013 UCI Mountain Bike World Cup #2 Val di Sole, [Italy]10th place, 2013 UCI Mountain Bike World Cup #3 Vallnord Andorra1st place, USAC National Downhill Pro Championships, Angel Fire New Mexico United States5th place, 2013 UCI Mountain Bike World Cup #4 Mont Saint Anne, Canada67th place, UCI MTB World Championships 2013 Pietermaritzburg, South Africa
1st place, 2014 UCI Mountain Bike World Cup #1 Pietermaritzburg, South Africa4th place, 2014 UCI Mountain Bike World Cup #2 Cairns, Australia4th place, 2014 UCI Mountain Bike World Cup #3 Fort William,78thplace, 2014 UCI Mountain Bike World Cup #4 Leogang, Austria1st place, USAC National Downhill Pro Championships, Angel Fire New Mexico United States6th place, 2014 UCI Mountain Bike World Cup #5 Mont Saint-Anne, Canada2nd place, 2014 UCI Mountain Bike World Cup #6 Windham, New York State, United States6th place, 2014 UCI Mountain Bike World Cup #7 Meribel, France14th place, UCI MTB World Championships 2014 Hafjell Bike Park, Norway
1st place, 2015 UCI Mountain Bike World Cup #1 Lourdes, France2nd place, 2015 UCI Mountain Bike World Cup #2 Fort William,1st place, 2015 UCI Mountain Bike World Cup #3 Leogang, Austria8th place, 2015 UCI Mountain Bike World Cup #4 Lenzerheide switzerland1st place, USAC National Downhill Pro Championships, Mammouth California United States7th place, 2015 UCI Mountain Bike World Cup #5 Mont Saint-Anne, Canada1st place, 2015 UCI Mountain Bike World Cup #6 Windham, New York State, United States1st place, 2015 UCI Mountain Bike World Cup #7 Val di Sole, [Italy]
1st place, 2016 UCI Mountain Bike World Cup #1 Lourdes, France4th place, 2016 UCI Mountain Bike World Cup #2 Cairns, Australia2nd place, 2016 UCI Mountain Bike World Cup #3 Fort William, Scotland1st place, 2016 UCI Mountain Bike World Cup #4 Leogang, Austria2nd place, 2016 UCI Mountain Bike World Cup #6 Mont Sainte-Anne, Quebec1st place, National Mountain Bike Championships - Downhill55th place, 2016 UCI Mountain Bike World Cup #7 Vallnord, Andorra1st place, Overall for the 2016 UCI Mountain Bike World Cup
Aaron Gwin Wikipedia
Similar TopicsFrom Soup to Nuts
Eckehard Specht
Desi Dobreva


text:
Home > Vocal > Vocal - Italian > Piero Visconti (Bongiovanni 1235)
Item# V2409
V2409. PIERO VISCONTI: Arias from Roberto Devereux, Lucrezia Borgia, L'Elisir, Martha, La Gioconda, Manon Lescaut, La Boheme, Tosca, Turandot, Luisa Miller, Rigoletto, Ballo & Aïda. (Italy) Bongiovanni 1235, Live Performances, 1976-92. Final Copy! - 8007068123520
CRITIC REVIEW:
"This Bongiovanni CD release finally renders justice to a singer forgotten by the recording industry and bereft of the notoriety that a quality career such as his should have afforded him. Piero Visconti was a tenor with everything in place to leave his mark on the opera history of the 1970s, 80s and beyond. Listening to these pieces, all from live recordings, is to discover a lost vocal treasure. After a promising decade his Italian engagements became fewer, making it inexplicably difficult to find his name on Italian playbills of the 90s, which should have been the time of his full artistic maturity."
- Bongiovanni


text:
Home > Latest News and Research > Press Office > Press Releases > Lords appointments mean House is on track for 1,000 members
Lords appointments mean House is on track for 1,000 members
Posted on the 22nd May 2018
Campaigners slam size of unelected chamber – and say House authorities should refuse to allocate time for introduction ceremonies.
Statement from the Electoral Reform Society for immediate release, 22nd May 2018.
Spokespeople are available for interview. For more information, contact [email protected] or 07717211630.
The House of Lords will be 'bursting at the seams' with more than 1,000 members by 2031 if its current rate of growth continues, according to Electoral Reform Society analysis.
It follows news that 13 new peers will be appointed to the unelected chamber, bringing the total number of Lords to 818 [1]. The announcement was 'cynically' timed to be buried by news of the Royal Wedding, say campaigners [2].
Membership has grown by over 21% since the turn of the century, from 675 to its current size – despite constant pledges to reform the second chamber. If this rate of growth were to continue, the number of peers would move past 1,000 sometime during the 2030-31 parliamentary session, the new analysis shows.
The total cost of the new Peers in terms of annual allowances and travel expenses is expected to be at least £289,558 a year, according to ERS analysis – based on the average claim of £22,273.69 for the circa 141 days the chamber sits each year.
However, a response to a Parliamentary question last year found the average cost of a peer – looking at the total cost of the House, minus works and building costs, is as much as £83,000 per year – suggesting the new appointees could have just added £1,079,000 to the overall annual bill.
Moreover, the appointments do little to change the political balance of the House. Given the current party balance in the chamber, Theresa May would need to appoint 768 more peers – in addition to the new appointments – before the party balance in the House reflected the results of the 2017 General Election [see methodology note].
In addition to being oversized, the House is now packed with 187 ex-MPs, 26 ex-MEPs and 31 ex-members of devolved institutions – which campaigners say rubbishes claims it is full of independent experts.
This latest batch of Lords appointments came despite a report commissioned by the Lord Speaker, Lord Fowler, proposing a 'two-out, one-in' system to bring the total down to 600 by 2027 at the end of last year [3].
In light of these findings, the Electoral Reform Society is calling for the House authorities to refuse to allocate time for the introduction ceremonies.
Darren Hughes, Chief Executive of the Electoral Reform Society, said:
"Despite parties claiming they want a smaller second chamber, they continue to pack the unelected house with yet more unwanted appointees. That the PM tried to cynically hide the news amid the joy of the Royal Wedding shows she knows public opinion is not with her on this one. Voters are sick of this private members club growing out of control.
"These new appointments have again reflected the unwillingness of politicians to act on this issue – and the inability of peers to regulate their own house. We've seen no action since the Burns' report last year, when Peers said they'd start work shrinking the second chamber. These latest additions make a mockery of those claims – and of our democracy.
"These new Peers will cost the taxpayer hundreds of thousands every year – at a time when many public services are being cut back.
"The House of Lords, even at its current size, is an affront to democracy – yet politicians keep packing it with more donors and former politicians.
"Our projections show at the current rate of growth the number of peers would surpass 1,000 in little over a decade. This rate of nepotism is simply not sustainable – the House will be bursting at the seams.
"If the Lords are serious about reducing size of upper chamber – as they say they are – they should refuse to schedule time for swearing in these new peers. Frankly these hyper-partisan appointments do a huge disservice to our democracy.
"Rather than dishing out immense sums rewarding ex-MPs and donors, we need a revising chamber that can truly command the public's support. Voters have had enough of the rolling expenses burden that is the second chamber – it's time for a much smaller, fairly-elected upper house.
"It is now time for cross-party legislation to finally reform this archaic and super-sized second chamber. We've had years of stalling on this front – Theresa May must now act."
The Electoral Reform Society has criticised the deeply cynical timing of the announcement which coincided with the news agenda being dominated by the Royal wedding.
It also criticised Jeremy Corbyn for seemingly backtracking on his previous stance on the House of Lords. [See note 2]
The House of Lords is already the second largest legislative chamber in the world, behind China's National People's Congress. There are more peers than could ever sit in the chamber at the same time and the bulk of the work of the House is done by a much smaller group of peers.
During a debate on the size of the House in December [4], several peers expressed embarrassment or discomfort about the size of their chamber.
[1] This figure includes 25 members who are currently not eligible to take part in the work of the House of Lords.
[2] https://www.electoral-reform.org.uk/latest-news-and-research/media-centre/press-releases/new-peers-announcement-cynically-timed-to-coincide-with-royal-wedding-say-campaigners/
[3] https://hansard.parliament.uk/lords/2017-12-19/debates/91B764B6-4EC8-4D7E-A07A-A28D0327E7B3/HouseOfLordsLordSpeaker%E2%80%99SCommitteeReport
[4] Lord Harries of Pentregarth, said: "I believe that our present size … brings us into disrepute. I feel embarrassed when someone enquires about our size, even when I stress that the average daily attendance is only about 484."
Lord Selkirk of Douglas, added: "It cannot sit altogether comfortably that when legislatures around the world are listed by size, we come second only to the National People's Congress of China."
Size projection
The starting point for this analysis was taken as 1999 when the House of Lords Act introduced by New Labour removed hundreds of hereditary peers. A dataset was built using the total number of peers each year since 1999, going off the annual growth rates for the Lords. A regression model was used from this data to project the size of the Lords into the future.
Party Balance
Because Peers cannot be forced to resign, the size of any political grouping in the House of Lords cannot be forcibly reduced. This means that, given the current imbalance in the chamber, in order to get each grouping in balance to its General Election vote, the Lords must grow dramatically. The Liberal Democrats are currently the most over-represented group in the Lords, with 18.0% of the Lords. To get this figure down to the proportion of the vote they won in 2017 (7.4%), the size of the Lords must grow by 768, following the new appointments.
Cost of the new peers
In the 2016/17 term, the average Peer received £22,273.69 in expenses and allowances (above the median UK take home pay). The average peer votes just 35 times per year, and speaks 34 times. See here:https://www.electoral-reform.org.uk/latest-news-and-research/media-centre/press-releases/scandal-of-silent-peers-revealed-1-3m-claimed-by-lords-who-never-speak/
For the higher figure, see House of Lords written questions response: https://www.parliament.uk/business/publications/written-questions-answers-statements/written-question/Lords/2017-02-24/HL5613/


Water tank compatible with the Francis Francis Y5, Y5 Duo, or Y5 Milk iperEspresso machines. Unplug the machine before cleaning. Wait until the machine is cool before cleaning. Remove the water tank by pulling it up slightly and then outward. Fill with fresh drinking water up to the MAX level. Push the water tank completely into the machine.


text:
Creators of mysterious metal monoliths discovered in the USA and Europe
BY Steve Cowan	 December 4, 2020 1:08 pm GMT+0300 December 4, 2020 1:11 pm GMT+0300 245 Views
In November, photos of a metal monolith in the desert area in Utah (USA) appeared online. Later, a similar monolith was found in Romania and California. And now, it seems, it became known who installs them. Mashable writes about this.
Users around the world had many options for who installed monoliths in different parts of the world. Someone pragmatically believed that an advertising brand was behind this; someone complained about aliens. But it seems that everything is a little more prosaic.
The Most Famous Artist community posted the photos on their website and social networks. By doing this, they confirm, even if not openly, that they are responsible for creating and installing objects. In any case, this conclusion is made in Mashable.
Публикация от The Most Famous Artist (@themostfamousartist)
The founder of the Most Famous Artist Matty Mo is known for works that question modern art's importance. For example, his team turned the "Hollywood" sign in Los Angeles into "Hollyweed." The latter can be translated as "Holy marijuana."
In the Instagram profile, @themostfamousartist features a photo of the monolith being wheeled from a warehouse. However, many users in the comments still did not believe in Mo's involvement in the performance. Later, a small video appeared in the profile showing a drawing of the monolith and a link to an article in Mashable.
And on the artist's website, there is a section where you can buy a similar monolith for yourself. The price of the issue is 45 thousand dollars. Delivery is promised to be arranged in four to six weeks.