<commit_before>/*
  Copyright (C) 2008 by <NAME>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "common.h"

#include "scene.h"
#include "object_terrain2.h"

namespace lighter
{
  Object_Terrain2::Object_Terrain2 (ObjectFactory_Terrain2* factory) : 
    Object (factory)
  {
    saverPluginName = "crystalspace.mesh.saver.terrain2";
  }

  bool Object_Terrain2::Initialize (Sector* sector)
  {
    if (!factory || !meshWrapper) return false;

    if (factory->hasTangents)
    {
      objFlags.Set (OBJECT_FLAG_TANGENTS);
      vdataBitangents = factory->vdataBitangents;
      vdataTangents = factory->vdataTangents;
    }

    this->sector = sector;

    const csReversibleTransform transform = meshWrapper->GetMovable ()->
      GetFullTransform ();

    //Copy over data, transform the radprimitives..
    vertexData.Transform (transform);
    primVertexData.Transform (transform);
    if (objFlags.Check (OBJECT_FLAG_TANGENTS))
    {
      for(size_t i = 0; i < vertexData.positions.GetSize (); ++i)
      {
        csVector3& tang = 
          *((csVector3*)vertexData.GetCustomData (i, vdataTangents));
        tang = transform.This2OtherRelative (tang);
        csVector3& bitang = 
          *((csVector3*)vertexData.GetCustomData (i, vdataBitangents));
        bitang = transform.This2OtherRelative (bitang);
      }
      
    }
    ComputeBoundingSphere ();

    return true;
  }

  void Object_Terrain2::ParseMesh (iMeshWrapper *wrapper)
  {
    csRef<iTerrainSystem> terrSys = scfQueryInterface<iTerrainSystem> (
      wrapper->GetMeshObject());
    if (!terrSys.IsValid()) return;

    Object::ParseMesh (wrapper);

    /* Abuse per-vertex lighting for terrain. Instead of tesselating
       terrain into a lot of small primitives (somewhat slow) compute lighting
       at the points on the terrain that correspond to lightmap pixels.
       So set up a "vertex" for each of these points and use PVL.
       Later, the data is copied into a lightmap image. */
    lightPerVertex = true;

    size_t numCells = terrSys->GetCellCount();
    for (size_t c = 0; c < numCells; c++)
    {
      iTerrainCell* cell = terrSys->GetCell (c, true);

      const csVector3& cellSize (cell->GetSize ());
      const csVector2& cellPos (cell->GetPosition ());

      uint lmSamplesX = csMin ((uint)csFindNearestPowerOf2 (
        cellSize.x * factory->GetLMDensity()),
        globalConfig.GetLMProperties().maxLightmapU);
      uint lmSamplesY = csMin ((uint)csFindNearestPowerOf2 (
        cellSize.z * factory->GetLMDensity()),
        globalConfig.GetLMProperties().maxLightmapV);

      {
	uint primSamplesX = cell->GetGridWidth();
	uint primSamplesY = cell->GetGridHeight();
	size_t numCellVerts = primSamplesX * primSamplesY;
	uint indexOffs = (uint)primVertexData.positions.GetSize();
	primVertexData.positions.SetCapacity (indexOffs + numCellVerts);
	primVertexData.uvs.SetCapacity (primVertexData.uvs.GetSize()
	  + numCellVerts);
	// @@@ FIXME: normals needed?
	primVertexData.normals.SetCapacity (primVertexData.normals.GetSize()
	  + numCellVerts);

	float invSamplesX = 1.0f/(primSamplesX-1);
	float invSamplesY = 1.0f/(primSamplesY-1);

	float posScaleX = cellSize.x * invSamplesX;
	float posScaleY = cellSize.z * invSamplesY;

	for (uint y = 0; y < primSamplesY; y++)
	{
	  for (uint x = 0; x < primSamplesX; x++)
	  {
	    csVector2 p (x, y);

	    float height = cell->GetHeight (p) - 0.01f;
	    csVector3 norm (cell->GetNormal (p));
	    csVector2 uv (p.x * invSamplesX, p.y * invSamplesY);

	    primVertexData.positions.Push (csVector3 (
	      x * posScaleX + cellPos.x, height, y * posScaleY + cellPos.y));
	    primVertexData.uvs.Push (uv);
	    primVertexData.normals.Push (norm);
	  }
	}

	size_t numIndices = (primSamplesX - 1) * (primSamplesY - 1) * 2;
	PrimitiveArray& cellPrimitives = allPrimitives.GetExtend (
	  allPrimitives.GetSize());
	cellPrimitives.SetCapacity (numIndices);
	for (uint y = 0; y < primSamplesY-1; y++)
	{
	  for (uint x = 0; x < primSamplesX-1; x++)
	  {
	    Primitive prim1 (primVertexData);
	    prim1.GetTriangle().a = indexOffs + (y*primSamplesX) + x;
	    prim1.GetTriangle().b = indexOffs + (y*primSamplesX) + x + 1;
	    prim1.GetTriangle().c = indexOffs + ((y+1)*primSamplesX) + x;
	    prim1.ComputePlane ();

	    Primitive prim2 (primVertexData);
	    prim2.GetTriangle().a = indexOffs + ((y+1)*primSamplesX) + x;
	    prim2.GetTriangle().b = indexOffs + (y*primSamplesX) + x + 1;
	    prim2.GetTriangle().c = indexOffs + ((y+1)*primSamplesX) + x + 1;
	    prim2.ComputePlane ();

	    cellPrimitives.Push (prim1);
	    cellPrimitives.Push (prim2);
	  }
	}
      }

      {
	float lmScaleX = float (cell->GetGridWidth())/float (lmSamplesX);
	float lmScaleY = float (cell->GetGridHeight())/float (lmSamplesY);

	float invSamplesX = 1.0f/(lmSamplesX);
	float invSamplesY = 1.0f/(lmSamplesY);

	float pScaleX = cellSize.x * invSamplesX;
	float pScaleY = cellSize.z * invSamplesY;

	for (uint y = 0; y < lmSamplesY; y++)
	{
	  for (uint x = 0; x < lmSamplesX; x++)
	  {
	    csVector2 p ((x + 0.5f) * lmScaleX, (y + 0.5f) * lmScaleY);

	    float height = cell->GetHeight (p);
	    csVector3 norm (cell->GetNormal (p));

	    vertexData.positions.Push (csVector3 (
	      (x + 0.5f) * pScaleX + cellPos.x, height, 
	      (y + 0.5f) * pScaleY + cellPos.y));
	    vertexData.normals.Push (norm);
	  }
	}

	LMDimensions lmDim;
	lmDim.w = lmSamplesX;
	lmDim.h = lmSamplesY;
	lmDims.Push (lmDim);
      }

      cell->SetLoadState (iTerrainCell::NotLoaded);
    }
  }

  void Object_Terrain2::SaveMesh (iDocumentNode *node)
  {
    csRef<iTerrainSystem> terrSys = scfQueryInterface<iTerrainSystem> (
      meshWrapper->GetMeshObject());
    if (!terrSys.IsValid()) return;
    csRef<iTerrainFactory> terrFact = scfQueryInterface<iTerrainFactory> (
      meshWrapper->GetFactory()->GetMeshObjectFactory());
    if (!terrFact.IsValid()) return;

    CS::ShaderVarName lightmapName[4] =
    { 
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap"),
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap dir 1"),
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap dir 2"),
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap dir 3")<commit_msg>Correctly translate terrain2 height values to world space (height grid Y axis is inverted relative to world space); avoid self-shadowing artifacts<commit_after>/*
  Copyright (C) 2008 by <NAME>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "common.h"

#include "scene.h"
#include "object_terrain2.h"

namespace lighter
{
  Object_Terrain2::Object_Terrain2 (ObjectFactory_Terrain2* factory) : 
    Object (factory)
  {
    saverPluginName = "crystalspace.mesh.saver.terrain2";
  }

  bool Object_Terrain2::Initialize (Sector* sector)
  {
    if (!factory || !meshWrapper) return false;

    if (factory->hasTangents)
    {
      objFlags.Set (OBJECT_FLAG_TANGENTS);
      vdataBitangents = factory->vdataBitangents;
      vdataTangents = factory->vdataTangents;
    }

    this->sector = sector;

    const csReversibleTransform transform = meshWrapper->GetMovable ()->
      GetFullTransform ();

    //Copy over data, transform the radprimitives..
    vertexData.Transform (transform);
    primVertexData.Transform (transform);
    if (objFlags.Check (OBJECT_FLAG_TANGENTS))
    {
      for(size_t i = 0; i < vertexData.positions.GetSize (); ++i)
      {
        csVector3& tang = 
          *((csVector3*)vertexData.GetCustomData (i, vdataTangents));
        tang = transform.This2OtherRelative (tang);
        csVector3& bitang = 
          *((csVector3*)vertexData.GetCustomData (i, vdataBitangents));
        bitang = transform.This2OtherRelative (bitang);
      }
      
    }
    ComputeBoundingSphere ();

    return true;
  }

  void Object_Terrain2::ParseMesh (iMeshWrapper *wrapper)
  {
    csRef<iTerrainSystem> terrSys = scfQueryInterface<iTerrainSystem> (
      wrapper->GetMeshObject());
    if (!terrSys.IsValid()) return;

    Object::ParseMesh (wrapper);

    /* Abuse per-vertex lighting for terrain. Instead of tesselating
       terrain into a lot of small primitives (somewhat slow) compute lighting
       at the points on the terrain that correspond to lightmap pixels.
       So set up a "vertex" for each of these points and use PVL.
       Later, the data is copied into a lightmap image. */
    lightPerVertex = true;

    size_t numCells = terrSys->GetCellCount();
    for (size_t c = 0; c < numCells; c++)
    {
      iTerrainCell* cell = terrSys->GetCell (c, true);

      const csVector3& cellSize (cell->GetSize ());
      const csVector2& cellPos (cell->GetPosition ());

      uint lmSamplesX = csMin ((uint)csFindNearestPowerOf2 (
        int (cellSize.x * factory->GetLMDensity())),
        globalConfig.GetLMProperties().maxLightmapU);
      uint lmSamplesY = csMin ((uint)csFindNearestPowerOf2 (
        int (cellSize.z * factory->GetLMDensity())),
        globalConfig.GetLMProperties().maxLightmapV);

      {
	uint primSamplesX = cell->GetGridWidth();
	uint primSamplesY = cell->GetGridHeight();
	size_t numCellVerts = primSamplesX * primSamplesY;
	uint indexOffs = (uint)primVertexData.positions.GetSize();
	primVertexData.positions.SetCapacity (indexOffs + numCellVerts);
	primVertexData.uvs.SetCapacity (primVertexData.uvs.GetSize()
	  + numCellVerts);
	// @@@ FIXME: normals needed?
	primVertexData.normals.SetCapacity (primVertexData.normals.GetSize()
	  + numCellVerts);

	float invSamplesX = 1.0f/(primSamplesX-1);
	float invSamplesY = 1.0f/(primSamplesY-1);

	float posScaleX = cellSize.x * invSamplesX;
	float posScaleY = cellSize.z * invSamplesY;

	for (uint y = 0; y < primSamplesY; y++)
	{
	  for (uint x = 0; x < primSamplesX; x++)
	  {
	    // Bit of a hack to avoid terrain self-shadowing acne
	    float height = FLT_MAX;
	    for (int dx = -1; dx < 2; dx++)
	    {
	      if (int (x) + dx < 0) continue;
	      if (x + dx >= primSamplesX) continue;
	      for (int dy = -1; dy < 2; dy++)
	      {
		if (int (y) + dy < 0) continue;
		if (y + dy >= primSamplesX) continue;
		csVector2 p (x * posScaleX, y * posScaleY);
		
		height = csMin (height, cell->GetHeight (x+dx, y+dy) - 0.01f);
	      }
	    }
	  
	    csVector2 p (x * posScaleX, y * posScaleY);

	    //float height = cell->GetHeight (x, y) - EPSILON;
	    csVector3 norm (cell->GetNormal (x, y));
	    csVector2 uv (p.x * invSamplesX, p.y * invSamplesY);

	    primVertexData.positions.Push (csVector3 (
	      p.x + cellPos.x, height, cellSize.z - p.y + cellPos.y));
	    primVertexData.uvs.Push (uv);
	    primVertexData.normals.Push (norm);
	  }
	}

	size_t numIndices = (primSamplesX - 1) * (primSamplesY - 1) * 2;
	PrimitiveArray& cellPrimitives = allPrimitives.GetExtend (
	  allPrimitives.GetSize());
	cellPrimitives.SetCapacity (numIndices);
	for (uint y = 0; y < primSamplesY-1; y++)
	{
	  for (uint x = 0; x < primSamplesX-1; x++)
	  {
	    Primitive prim1 (primVertexData);
	    prim1.GetTriangle().a = indexOffs + (y*primSamplesX) + x;
	    prim1.GetTriangle().b = indexOffs + (y*primSamplesX) + x + 1;
	    prim1.GetTriangle().c = indexOffs + ((y+1)*primSamplesX) + x;
	    prim1.ComputePlane ();

	    Primitive prim2 (primVertexData);
	    prim2.GetTriangle().a = indexOffs + ((y+1)*primSamplesX) + x;
	    prim2.GetTriangle().b = indexOffs + (y*primSamplesX) + x + 1;
	    prim2.GetTriangle().c = indexOffs + ((y+1)*primSamplesX) + x + 1;
	    prim2.ComputePlane ();

	    cellPrimitives.Push (prim1);
	    cellPrimitives.Push (prim2);
	  }
	}
      }

      {
	float invSamplesX = 1.0f/(lmSamplesX);
	float invSamplesY = 1.0f/(lmSamplesY);

	float pScaleX = cellSize.x * invSamplesX;
	float pScaleY = cellSize.z * invSamplesY;

	for (uint y = 0; y < lmSamplesY; y++)
	{
	  for (uint x = 0; x < lmSamplesX; x++)
	  {
	    csVector2 p ((x + 0.5f) * pScaleX, (y + 0.5f) * pScaleY);

	    float height = cell->GetHeight (p);
	    csVector3 norm (cell->GetNormal (p));

	    vertexData.positions.Push (csVector3 (
	      p.x + cellPos.x, height, 
	      cellSize.z - p.y + cellPos.y));
	    vertexData.normals.Push (norm);
	  }
	}

	LMDimensions lmDim;
	lmDim.w = lmSamplesX;
	lmDim.h = lmSamplesY;
	lmDims.Push (lmDim);
      }

      cell->SetLoadState (iTerrainCell::NotLoaded);
    }
  }

  void Object_Terrain2::SaveMesh (iDocumentNode *node)
  {
    csRef<iTerrainSystem> terrSys = scfQueryInterface<iTerrainSystem> (
      meshWrapper->GetMeshObject());
    if (!terrSys.IsValid()) return;
    csRef<iTerrainFactory> terrFact = scfQueryInterface<iTerrainFactory> (
      meshWrapper->GetFactory()->GetMeshObjectFactory());
    if (!terrFact.IsValid()) return;

    CS::ShaderVarName lightmapName[4] =
    { 
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap"),
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap dir 1"),
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap dir 2"),
      CS::ShaderVarName (globalLighter->svStrings, "tex lightmap dir 3")
{'fraction_non_alphanumeric': 0.12043821087153722, 'fraction_numerical': 0.013397529830437514, 'mean_word_length': 3.322074788902292, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 76, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2124568', 'n_tokens_mistral': 5112, 'n_tokens_neox': 4715, 'n_words': 1214}