Ticket #794: Camera.patch

File Camera.patch, 21.4 KB (added by Dietger, 12 years ago)

Makes camera zoom a local distance to trilinear filtered heightmap. Filter radius is proportional to zoom distance. Uses a mipmap for fast filtering.

  • binaries/data/config/default.cfg

     
    9595view.near = 2.0                             ; Near plane distance
    9696view.far = 4096.0                           ; Far plane distance
    9797view.fov = 45.0                             ; Field of view (degrees), lower is narrow, higher is wide
     98view.height.smoothness = 0.5
     99view.height.min = 16
    98100
    99101; HOTKEY MAPPINGS:
    100102
  • source/graphics/GameView.cpp

     
    190190        JoystickRotateY(-1),
    191191        JoystickZoomIn(-1),
    192192        JoystickZoomOut(-1),
     193        HeightSmoothness(0.5f),
     194        HeightMin(16.f),
    193195
    194196        PosX(0, 0, 0.01f),
    195197        PosY(0, 0, 0.01f),
     
    289291    int JoystickRotateY;
    290292    int JoystickZoomIn;
    291293    int JoystickZoomOut;
     294    float HeightSmoothness;
     295    float HeightMin;
    292296
    293297    ////////////////////////////////////////
    294298    // Camera Controls State
     
    426430    CFG_GET_SYS_VAL("joystick.camera.zoom.in", Int, m->JoystickZoomIn);
    427431    CFG_GET_SYS_VAL("joystick.camera.zoom.out", Int, m->JoystickZoomOut);
    428432
     433    CFG_GET_SYS_VAL("view.height.smoothness", Float, m->HeightSmoothness);
     434    CFG_GET_SYS_VAL("view.height.min", Float, m->HeightMin);
     435
    429436    CFG_GET_SYS_VAL("view.pos.smoothness", Float, m->PosX.m_Smoothness);
    430437    CFG_GET_SYS_VAL("view.pos.smoothness", Float, m->PosY.m_Smoothness);
    431438    CFG_GET_SYS_VAL("view.pos.smoothness", Float, m->PosZ.m_Smoothness);
     
    595602    g_Renderer.GetWaterManager()->UnloadWaterTextures();
    596603}
    597604
     605static void FocusHeight(CGameViewImpl* m, bool smooth)
     606{
     607    /*
     608        The camera pivot height is moved towards ground level.
     609        To prevent excessive zoom when looking over a cliff,
     610        the target ground level is the maximum of the ground level at the camera's near and pivot points.
     611        The ground levels are filtered to achieve smooth camera movement.
     612        The filter radius is proportional to the zoom level.
     613        The camera height is clamped to prevent map penetration.
     614    */
    598615
    599 static void ClampDistance(CGameViewImpl* m, bool smooth)
    600 {
    601616    if (!m->ConstrainCamera)
    602617        return;
    603618
    604619    CCamera targetCam = m->ViewCamera;
    605620    SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
    606621
    607     CVector3D forwards = targetCam.m_Orientation.GetIn();
     622    const CVector3D position = targetCam.m_Orientation.GetTranslation();
     623    const CVector3D forwards = targetCam.m_Orientation.GetIn();
    608624
    609     CVector3D delta = targetCam.GetFocus() - targetCam.m_Orientation.GetTranslation();
     625    // horizontal view radius
     626    const float radius = hypot( forwards.X, forwards.Z ) * m->Zoom.GetSmoothedValue();
     627    const float near_radius = radius * m->HeightSmoothness;
     628    const float pivot_radius = radius * m->HeightSmoothness;
    610629
    611     float dist = delta.Dot(forwards);
    612     float clampedDist = Clamp(dist, m->ViewZoomMin, m->ViewZoomMax);
    613     float diff = clampedDist - dist;
     630    const CVector3D nearPoint = position + forwards * m->ViewNear;
     631    const CVector3D pivotPoint = position + forwards * m->Zoom.GetSmoothedValue();
    614632
     633    const float ground = m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z);
     634
     635    // filter ground levels for smooth camera movement
     636    const float filtered_near_ground = m->Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(nearPoint.X, nearPoint.Z, near_radius);
     637    const float filtered_pivot_ground = m->Game->GetWorld()->GetTerrain()->GetFilteredGroundLevel(pivotPoint.X, pivotPoint.Z, pivot_radius);
     638   
     639    // filtered maximum visible ground level in view
     640    const float filtered_ground = std::max<float>(filtered_near_ground, filtered_pivot_ground);
     641
     642    // target camera height above pivot point
     643    const float pivot_height = -forwards.Y * (m->Zoom.GetSmoothedValue() - m->ViewNear);
     644    // minimum camera height above filtered ground level
     645    const float min_height = (m->HeightMin + ground - filtered_ground);
     646
     647    const float target_height = std::max<float>(pivot_height , min_height);
     648    const float height = (nearPoint.Y - filtered_ground);
     649    const float diff = target_height - height;
     650
    615651    if (!diff)
    616652        return;
    617653
    618654    if (smooth)
    619655    {
    620         m->PosX.AddSmoothly(forwards.X * -diff);
    621         m->PosY.AddSmoothly(forwards.Y * -diff);
    622         m->PosZ.AddSmoothly(forwards.Z * -diff);
     656        m->PosY.AddSmoothly(diff);
    623657    }
    624658    else
    625659    {
    626         m->PosX.Add(forwards.X * -diff);
    627         m->PosY.Add(forwards.Y * -diff);
    628         m->PosZ.Add(forwards.Z * -diff);
     660        m->PosY.Add(diff);
    629661    }
    630662}
    631663
     664CVector3D CGameView::GetSmoothPivot(CCamera &camera) const
     665{
     666    ENSURE( m != 0 );
     667    return camera.m_Orientation.GetTranslation() + camera.m_Orientation.GetIn() * m->Zoom.GetSmoothedValue();
     668}
     669
    632670void CGameView::Update(float DeltaTime)
    633671{
    634672    // If camera movement is being handled by the touch-input system,
     
    758796                // Move the camera to match the unit
    759797                CCamera targetCam = m->ViewCamera;
    760798                SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation);
    761 
    762                 CVector3D pivot = targetCam.GetFocus();
     799               
     800                CVector3D pivot = GetSmoothPivot(targetCam);
    763801                CVector3D delta = pos - pivot;
    764802                m->PosX.AddSmoothly(delta.X);
    765803                m->PosY.AddSmoothly(delta.Y);
     
    774812    }
    775813
    776814    if (HotkeyIsPressed("camera.zoom.in"))
     815        m->Zoom.AddSmoothly(-m->ViewZoomSpeed * DeltaTime);
     816    if (HotkeyIsPressed("camera.zoom.out"))
    777817        m->Zoom.AddSmoothly(m->ViewZoomSpeed * DeltaTime);
    778     if (HotkeyIsPressed("camera.zoom.out"))
    779         m->Zoom.AddSmoothly(-m->ViewZoomSpeed * DeltaTime);
    780818
    781     float zoomDelta = m->Zoom.Update(DeltaTime);
     819    if (m->ConstrainCamera)
     820        m->Zoom.ClampSmoothly( m->ViewZoomMin, m->ViewZoomMax );
     821
     822    float zoomDelta = -m->Zoom.Update(DeltaTime);
    782823    if (zoomDelta)
    783824    {
    784825        CVector3D forwards = m->ViewCamera.m_Orientation.GetIn();
     
    790831    if (m->ConstrainCamera)
    791832        m->RotateX.ClampSmoothly(DEGTORAD(m->ViewRotateXMin), DEGTORAD(m->ViewRotateXMax));
    792833
    793     ClampDistance(m, true);
     834    FocusHeight(m, true);
    794835
    795836    // Ensure the ViewCamera focus is inside the map with the chosen margins
    796837    // if not so - apply margins to the camera
     
    801842
    802843        CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain();
    803844
    804         CVector3D pivot = targetCam.GetFocus();
     845        CVector3D pivot = GetSmoothPivot(targetCam);
    805846        CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
    806847
    807848        CVector3D desiredPivot = pivot;
     
    846887
    847888            CVector3D upwards(0.0f, 1.0f, 0.0f);
    848889
    849             CVector3D pivot = targetCam.GetFocus();
     890            CVector3D pivot = GetSmoothPivot(targetCam);
    850891            CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
    851892
    852893            CQuaternion q;
     
    869910        {
    870911            CVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f;
    871912
    872             CVector3D pivot = m->ViewCamera.GetFocus();
     913            CVector3D pivot = GetSmoothPivot(targetCam);
    873914            CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot;
    874915
    875916            CQuaternion q;
     
    919960    CCamera targetCam = m->ViewCamera;
    920961    SetupCameraMatrixNonSmooth(m, &targetCam.m_Orientation);
    921962
    922     CVector3D pivot = targetCam.GetFocus();
     963    CVector3D pivot = GetSmoothPivot(targetCam);
    923964    CVector3D delta = target - pivot;
    924965   
    925966    m->PosX.SetValueSmoothly(delta.X + m->PosX.GetValue());
    926967    m->PosZ.SetValueSmoothly(delta.Z + m->PosZ.GetValue());
    927968
    928     ClampDistance(m, false);
     969    FocusHeight(m, false);
    929970
    930971    // Break out of following mode so the camera really moves to the target
    931972    m->FollowEntity = INVALID_ENTITY;
     
    944985    m->PosZ.SetValue(target.Z - delta.Z);
    945986    m->RotateX.SetValue(DEGTORAD(m->ViewRotateXDefault));
    946987    m->RotateY.SetValue(DEGTORAD(m->ViewRotateYDefault));
     988    m->Zoom.SetValue(m->ViewZoomDefault);
    947989
    948     ClampDistance(m, false);
     990    FocusHeight(m, false);
    949991
    950992    SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation);
    951993    m->ViewCamera.UpdateFrustum();
     
    9611003
    9621004    // Compute the zoom adjustment to get us back to the default
    9631005    CVector3D forwards = targetCam.m_Orientation.GetIn();
    964     CVector3D delta = targetCam.GetFocus() - targetCam.m_Orientation.GetTranslation();
     1006
     1007    CVector3D pivot = GetSmoothPivot(targetCam);
     1008    CVector3D delta = pivot - targetCam.m_Orientation.GetTranslation();
    9651009    float dist = delta.Dot(forwards);
    9661010    m->Zoom.AddSmoothly(dist - m->ViewZoomDefault);
    9671011
     
    10491093        // and we never get to see the "down" state inside Update().
    10501094        else if (hotkey == "camera.zoom.wheel.in")
    10511095        {
    1052             m->Zoom.AddSmoothly(m->ViewZoomSpeedWheel);
     1096            m->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel);
    10531097            return IN_HANDLED;
    10541098        }
    10551099        else if (hotkey == "camera.zoom.wheel.out")
    10561100        {
    1057             m->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel);
     1101            m->Zoom.AddSmoothly(m->ViewZoomSpeedWheel);
    10581102            return IN_HANDLED;
    10591103        }
    10601104        else if (hotkey == "camera.rotate.wheel.cw")
  • source/graphics/GameView.h

     
    8585    void CameraFollow(entity_id_t entity, bool firstPerson);
    8686    entity_id_t GetFollowedEntity();
    8787
     88    CVector3D GetSmoothPivot(CCamera &camera) const;
     89
    8890    float GetNear() const;
    8991    float GetFar() const;
    9092    float GetFOV() const;
  • source/graphics/HeightMipmap.cpp

     
     1/* Copyright (C) 2012 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "precompiled.h"
     19
     20#include "HeightMipmap.h"
     21#include "lib/bits.h"
     22
     23CHeightMipmap::CHeightMipmap()
     24{};
     25
     26CHeightMipmap::~CHeightMipmap()
     27{
     28    ReleaseData();
     29}
     30
     31void CHeightMipmap::ReleaseData()
     32{
     33    for (size_t i = 0 ; i < m_Mipmap.size(); ++i)
     34    {
     35        delete [] m_Mipmap[i].m_Heightmap;
     36        m_Mipmap[i].m_MapSize = 0;
     37    }
     38    m_Mipmap.clear();
     39}
     40
     41void CHeightMipmap::Update(const u16* ptr)
     42{
     43    ENSURE( ptr != 0 );
     44
     45    Update(ptr, 0, 0, m_MapSize, m_MapSize);
     46}
     47
     48void CHeightMipmap::Update(const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)
     49{
     50    ENSURE( ptr != 0 );
     51
     52    size_t mapSize = m_MapSize;
     53   
     54    for (size_t i = 0; i < m_Mipmap.size(); ++i)
     55    {
     56        // update window
     57        left = clamp<size_t>( (size_t)floorf((left / mapSize) * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize - 1 );
     58        bottom = clamp<size_t>( (size_t)floorf((bottom / mapSize) * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize - 1 );
     59
     60        right = clamp<size_t>( (size_t)ceilf((right / mapSize) * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize );
     61        top = clamp<size_t>( (size_t)ceilf((top / mapSize) * m_Mipmap[i].m_MapSize), 0, m_Mipmap[i].m_MapSize );
     62
     63        // update mipmap
     64        BilinearUpdate(m_Mipmap[i], mapSize, ptr, left, bottom, right, top);
     65
     66        mapSize = m_Mipmap[i].m_MapSize;
     67        ptr = m_Mipmap[i].m_Heightmap;
     68    }
     69}
     70
     71void CHeightMipmap::Initialize(size_t mapSize, const u16* ptr)
     72{
     73    ENSURE( ptr != 0 );
     74    ENSURE( mapSize > 0 );
     75
     76    ReleaseData();
     77
     78    m_MapSize = mapSize;
     79    size_t mipmapSize = round_down_to_pow2(mapSize);
     80
     81    while (mipmapSize > 1)
     82    {
     83        m_Mipmap.push_back(SMipmap(mipmapSize, new u16[mipmapSize*mipmapSize]));
     84        mipmapSize >>= 1;
     85    };
     86
     87    Update(ptr);
     88}
     89
     90float CHeightMipmap::GetTrilinearGroundLevel(float x, float z, float radius) const
     91{
     92    const float y = clamp<float>(logf(radius * m_Mipmap[0].m_MapSize) / logf(2), 0, m_Mipmap.size());
     93    const size_t iy = (size_t)clamp<ssize_t>((ssize_t)floorf(y), 0, m_Mipmap.size() - 1);
     94
     95    const float fy = y - iy;
     96
     97    const float h0 = BilinearFilter(m_Mipmap[iy], x, z);
     98    const float h1 = BilinearFilter(m_Mipmap[iy + 1], x, z);
     99
     100    return (1 - fy) * h0 + fy * h1;
     101}
     102
     103float CHeightMipmap::BilinearFilter(const SMipmap &mipmap, float x, float z) const
     104{
     105    x *= mipmap.m_MapSize;
     106    z *= mipmap.m_MapSize;
     107
     108    const size_t xi = (size_t)clamp<ssize_t>((ssize_t)floor(x), 0, mipmap.m_MapSize - 1);
     109    const size_t zi = (size_t)clamp<ssize_t>((ssize_t)floor(z), 0, mipmap.m_MapSize - 1);
     110
     111    const float xf = clamp<float>(x-xi, 0.0f, 1.0f);
     112    const float zf = clamp<float>(z-zi, 0.0f, 1.0f);
     113
     114    const float h00 = mipmap.m_Heightmap[zi*mipmap.m_MapSize + xi];
     115    const float h01 = mipmap.m_Heightmap[(zi+1)*mipmap.m_MapSize + xi];
     116    const float h10 = mipmap.m_Heightmap[zi*mipmap.m_MapSize + (xi+1)];
     117    const float h11 = mipmap.m_Heightmap[(zi+1)*mipmap.m_MapSize + (xi+1)];
     118
     119    return
     120        (1.f - xf) * (1.f - zf) * h00 +
     121               xf  * (1.f - zf) * h10 +
     122        (1.f - xf) *        zf  * h01 +
     123               xf  *        zf  * h11;
     124}
     125
     126void CHeightMipmap::HalfResizeUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)
     127{
     128    // specialized, faster version of BilinearUpdate for powers of 2
     129
     130    ENSURE(out_mipmap.m_MapSize != 0);
     131
     132    if (out_mipmap.m_MapSize * 2 != mapSize)
     133        debug_warn(L"wrong size");
     134
     135    // valid update window
     136    ENSURE(left < out_mipmap.m_MapSize );
     137    ENSURE(bottom < out_mipmap.m_MapSize );
     138    ENSURE(right > left && right <= out_mipmap.m_MapSize );
     139    ENSURE(top > bottom && top <= out_mipmap.m_MapSize );
     140
     141    for (size_t dstZ = bottom; dstZ < top; ++dstZ)
     142    for (size_t dstX = left; dstX < right; ++dstX)
     143    {
     144        size_t srcX = dstX << 1;
     145        size_t srcZ = dstZ << 1;
     146
     147        u16 h00 = ptr[srcX + 0 + srcZ * mapSize];
     148        u16 h10 = ptr[srcX + 1 + srcZ * mapSize];
     149        u16 h01 = ptr[srcX + 0 + (srcZ + 1) * mapSize];
     150        u16 h11 = ptr[srcX + 1 + (srcZ + 1) * mapSize];
     151       
     152        out_mipmap.m_Heightmap[dstX + dstZ * out_mipmap.m_MapSize] = (h00 + h10 + h01 + h11) / 4;
     153    }
     154}
     155
     156void CHeightMipmap::BilinearUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top)
     157{
     158    ENSURE(out_mipmap.m_MapSize != 0);
     159
     160    // filter should have full coverage
     161    ENSURE(out_mipmap.m_MapSize <= mapSize && out_mipmap.m_MapSize * 2 >= mapSize);
     162
     163    // valid update window
     164    ENSURE(left < out_mipmap.m_MapSize );
     165    ENSURE(bottom < out_mipmap.m_MapSize );
     166    ENSURE(right > left && right <= out_mipmap.m_MapSize );
     167    ENSURE(top > bottom && top <= out_mipmap.m_MapSize );
     168
     169    if (out_mipmap.m_MapSize * 2 == mapSize)
     170    {
     171        // optimized for powers of 2
     172        HalfResizeUpdate(out_mipmap, mapSize, ptr, left, bottom, right, top);
     173    }
     174    else
     175    {
     176        for (size_t dstZ = bottom; dstZ < top; ++dstZ)
     177        for (size_t dstX = left; dstX < right; ++dstX) {
     178            const float x = ((float)dstX / (float)out_mipmap.m_MapSize) * mapSize;
     179            const float z = ((float)dstZ / (float)out_mipmap.m_MapSize) * mapSize;
     180
     181            const size_t srcX = clamp<size_t>( (size_t)x, 0, mapSize - 1);
     182            const size_t srcZ = clamp<size_t>( (size_t)z, 0, mapSize - 1);
     183
     184            const float fx = clamp<float>(x - srcX, 0.0f, 1.0f);
     185            const float fz = clamp<float>(z - srcZ, 0.0f, 1.0f);
     186
     187            const float h00 = ptr[srcX + 0 + srcZ * mapSize];
     188            const float h10 = ptr[srcX + 1 + srcZ * mapSize];
     189            const float h01 = ptr[srcX + 0 + (srcZ + 1) * mapSize];
     190            const float h11 = ptr[srcX + 1 + (srcZ + 1) * mapSize];
     191           
     192            out_mipmap.m_Heightmap[dstX + dstZ * out_mipmap.m_MapSize] = (u16)
     193                ((1.f - fx) * (1.f - fz) * h00 +
     194                        fx  * (1.f - fz) * h10 +
     195                 (1.f - fx) *        fz  * h01 +
     196                        fx  *        fz  * h11);
     197        }
     198    }
     199}
  • source/graphics/HeightMipmap.h

     
     1/* Copyright (C) 2011 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18
     19/*
     20 * Describes ground using heightmap mipmaps
     21 * Used for camera movement
     22 */
     23
     24#ifndef INCLUDED_HEIGHTMIPMAP
     25#define INCLUDED_HEIGHTMIPMAP
     26
     27#include <cmath>
     28#include "maths/MathUtil.h"
     29#include "ps/CLogger.h"
     30#include "ps/Profile.h"
     31
     32struct SMipmap
     33{
     34    SMipmap()
     35    {
     36        m_MapSize = 0;
     37        m_Heightmap = 0;
     38    };
     39
     40    SMipmap(size_t MapSize,u16* Heightmap) : m_MapSize(MapSize), m_Heightmap(Heightmap)
     41    {}
     42
     43    size_t m_MapSize;
     44    u16* m_Heightmap;
     45};
     46
     47class CHeightMipmap
     48{
     49    NONCOPYABLE(CHeightMipmap);
     50public:
     51
     52    CHeightMipmap();
     53    ~CHeightMipmap();
     54
     55    void Initialize(size_t mapSize, const u16* ptr);
     56    void ReleaseData();
     57
     58    // update (a rectangular piece of) the heightmap mipmaps
     59    void Update(const u16* ptr);
     60    void Update(const u16* ptr, size_t left, size_t bottom, size_t right, size_t top);
     61   
     62    float GetTrilinearGroundLevel(float x, float z, float radius) const;
     63
     64private:
     65
     66    // get bilinear filtered height from mipmap
     67    float BilinearFilter(const SMipmap &mipmap, float x, float z) const;
     68       
     69    // update rectangle of the output mipmap by bilinear interpolating an input mipmap of exactly twice its size
     70    void HalfResizeUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top);
     71   
     72    // update rectangle of the output mipmap by bilinear interpolating the input mipmap
     73    void BilinearUpdate(SMipmap &out_mipmap, size_t mapSize, const u16* ptr, size_t left, size_t bottom, size_t right, size_t top);
     74   
     75    // size of this map in each direction
     76    size_t m_MapSize;
     77
     78    // mipmap list
     79    std::vector<SMipmap> m_Mipmap;
     80
     81};
     82
     83#endif
     84 No newline at end of file
  • source/graphics/Terrain.cpp

     
    5757// ReleaseData: delete any data allocated by this terrain
    5858void CTerrain::ReleaseData()
    5959{
     60    m_HeightMipmap.ReleaseData();
     61
    6062    delete[] m_Heightmap;
    6163    delete[] m_Patches;
    6264}
     
    8991    // setup patch parents, indices etc
    9092    InitialisePatches();
    9193
     94    // initialise mipmap
     95    m_HeightMipmap.Initialize(m_MapSize,m_Heightmap);
     96
    9297    return true;
    9398}
    9499
     
    330335    return fixed::FromInt(delta / TERRAIN_TILE_SIZE) / (int)HEIGHT_UNITS_PER_METRE;
    331336}
    332337
     338float CTerrain::GetFilteredGroundLevel(float x, float z, float radius) const
     339{
     340    // convert to [0,1] interval
     341    float nx = x / (TERRAIN_TILE_SIZE*m_MapSize);
     342    float nz = z / (TERRAIN_TILE_SIZE*m_MapSize);
     343    float nr = radius / (TERRAIN_TILE_SIZE*m_MapSize);
     344
     345    // get trilinear filtered mipmap height
     346    return HEIGHT_SCALE * m_HeightMipmap.GetTrilinearGroundLevel(nx, nz, nr);
     347}
     348
    333349float CTerrain::GetExactGroundLevel(float x, float z) const
    334350{
    335351    // Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
     
    530546
    531547    // initialise all the new patches
    532548    InitialisePatches();
     549
     550    // initialise mipmap
     551    m_HeightMipmap.Initialize(m_MapSize,m_Heightmap);
    533552}
    534553
    535554///////////////////////////////////////////////////////////////////////////////
     
    560579            patch->SetDirty(RENDERDATA_UPDATE_VERTICES);
    561580        }
    562581    }
     582
     583    // update mipmap
     584    m_HeightMipmap.Update(m_Heightmap);
    563585}
    564586
    565587
     
    589611        }
    590612    }
    591613
     614    // update mipmap
     615    m_HeightMipmap.Update(m_Heightmap,tx0,tz0,tx1,tz1);
     616
    592617    MakeDirty(tx0, tz0, tx1, tz1, RENDERDATA_UPDATE_VERTICES);
    593618
    594619    return avgY*HEIGHT_SCALE;
  • source/graphics/Terrain.h

     
    2525#include "maths/Vector3D.h"
    2626#include "maths/Fixed.h"
    2727#include "graphics/SColor.h"
     28#include "graphics/HeightMipmap.h"
    2829
    2930class CPatch;
    3031class CMiniPatch;
     
    8485    fixed GetVertexGroundLevelFixed(ssize_t i, ssize_t j) const;
    8586    float GetExactGroundLevel(float x, float z) const;
    8687    fixed GetExactGroundLevelFixed(fixed x, fixed z) const;
     88    float GetFilteredGroundLevel(float x, float z, float radius) const;
    8789
    8890    // get the approximate slope (0 = horizontal, 0.5 = 30 degrees, 1.0 = 45 degrees, etc)
    8991    fixed GetSlopeFixed(ssize_t i, ssize_t j) const;
     
    164166    u16* m_Heightmap;
    165167    // base colour (usually white)
    166168    SColor4ub m_BaseColour;
     169    // heightmap mipmap
     170    CHeightMipmap m_HeightMipmap;
    167171};
    168172
    169173#endif
  • source/lib/bits.h

     
    246246}
    247247
    248248/**
     249 * round down to next larger power of two.
     250 **/
     251template<typename T>
     252inline T round_down_to_pow2(T x)
     253{
     254    return T(1) << floor_log2(x);
     255}
     256
     257/**
    249258 * round number up/down to the next given multiple.
    250259 *
    251260 * @param n Number to round.