mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-29 16:20:11 -04:00
Support for setting a heightfield handle directly on the ShaderTerrainMesh
This commit is contained in:
parent
288452a861
commit
dac2bfc7b9
@ -12,26 +12,28 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Sets the path to the heightfield
|
||||
* @details This sets the path to the terrain heightfield. It should be 16bit
|
||||
* @brief Sets the heightfield texture
|
||||
* @details This sets the heightfield texture. It should be 16bit
|
||||
* single channel, and have a power-of-two resolution greater than 32.
|
||||
* Common sizes are 2048x2048 or 4096x4096.
|
||||
*
|
||||
* @param filename Path to the heightfield
|
||||
* You should call generate() after setting the heightfield.
|
||||
*
|
||||
* @param filename Heightfield texture
|
||||
*/
|
||||
INLINE void ShaderTerrainMesh::set_heightfield_filename(const Filename& filename) {
|
||||
_heightfield_source = filename;
|
||||
INLINE void ShaderTerrainMesh::set_heightfield(Texture* heightfield) {
|
||||
_heightfield_tex = heightfield;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the heightfield path
|
||||
* @details This returns the path of the terrain heightfield, previously set with
|
||||
* @brief Returns the heightfield
|
||||
* @details This returns the terrain heightfield, previously set with
|
||||
* set_heightfield()
|
||||
*
|
||||
* @return Path to the heightfield
|
||||
*/
|
||||
INLINE const Filename& ShaderTerrainMesh::get_heightfield_filename() const {
|
||||
return _heightfield_source;
|
||||
INLINE Texture* ShaderTerrainMesh::get_heightfield() const {
|
||||
return _heightfield_tex;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "typeHandle.h"
|
||||
|
||||
ConfigVariableBool stm_use_hexagonal_layout
|
||||
("stm-use-hexagonal-layout", true,
|
||||
("stm-use-hexagonal-layout", false,
|
||||
PRC_DESC("Set this to true to use a hexagonal vertex layout. This approximates "
|
||||
"the heightfield in a better way, however the CLOD transitions might be "
|
||||
"visible due to the vertices not matching exactly."));
|
||||
@ -81,7 +81,6 @@ ShaderTerrainMesh::ShaderTerrainMesh() :
|
||||
PandaNode("ShaderTerrainMesh"),
|
||||
_size(0),
|
||||
_chunk_size(32),
|
||||
_heightfield_source(""),
|
||||
_generate_patches(false),
|
||||
_data_texture(NULL),
|
||||
_chunk_geom(NULL),
|
||||
@ -107,7 +106,7 @@ ShaderTerrainMesh::ShaderTerrainMesh() :
|
||||
* @return true if the terrain was initialized, false if an error occured
|
||||
*/
|
||||
bool ShaderTerrainMesh::generate() {
|
||||
if (!do_load_heightfield())
|
||||
if (!do_check_heightfield())
|
||||
return false;
|
||||
|
||||
if (_chunk_size < 8 || !check_power_of_two(_chunk_size)) {
|
||||
@ -120,28 +119,29 @@ bool ShaderTerrainMesh::generate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
do_extract_heightfield();
|
||||
do_create_chunks();
|
||||
do_compute_bounds(&_base_chunk);
|
||||
do_create_chunk_geom();
|
||||
do_init_data_texture();
|
||||
do_convert_heightfield();
|
||||
|
||||
// Clear image after using it, otherwise we have two copies of the heightfield
|
||||
// in memory.
|
||||
_heightfield.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts the internal used PNMImage to a Texture
|
||||
* @details This converts the internal used PNMImage to a texture object. The
|
||||
* reason for this is, that we need the PNMimage for computing the chunk
|
||||
* bounds, but don't need it afterwards. However, since we have it in ram,
|
||||
* we can just put its contents into a Texture object, which enables the
|
||||
* user to call get_heightfield() instead of manually loading the texture
|
||||
* from disk again to set it as shader input (Panda does not cache PNMImages)
|
||||
* @brief Converts the internal used Texture to a PNMImage
|
||||
* @details This converts the texture passed with set_heightfield to a PNMImage,
|
||||
* so we can read the pixels in a fast way. This is only used while generating
|
||||
* the chunks, and the PNMImage is destroyed afterwards.
|
||||
*/
|
||||
void ShaderTerrainMesh::do_convert_heightfield() {
|
||||
_heightfield_tex = new Texture();
|
||||
_heightfield_tex->load(_heightfield);
|
||||
_heightfield_tex->set_keep_ram_image(true);
|
||||
void ShaderTerrainMesh::do_extract_heightfield() {
|
||||
nassertv(_heightfield_tex->has_ram_image()); // Heightfield not in RAM, extract ram image first
|
||||
|
||||
_heightfield_tex->store(_heightfield);
|
||||
|
||||
if (_heightfield.get_maxval() != 65535) {
|
||||
shader_terrain_cat.warning() << "Using non 16-bit heightfield!" << endl;
|
||||
@ -150,31 +150,23 @@ void ShaderTerrainMesh::do_convert_heightfield() {
|
||||
}
|
||||
_heightfield_tex->set_minfilter(SamplerState::FT_linear);
|
||||
_heightfield_tex->set_magfilter(SamplerState::FT_linear);
|
||||
_heightfield.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Intermal method to load the heightfield
|
||||
* @details This method loads the heightfield from the heightfield path,
|
||||
* @brief Intermal method to check the heightfield
|
||||
* @details This method cecks the heightfield generated from the heightfield texture,
|
||||
* and performs some basic checks, including a check for a power of two,
|
||||
* and same width and height.
|
||||
*
|
||||
* @return true if the heightfield was loaded and meets the requirements
|
||||
* @return true if the heightfield meets the requirements
|
||||
*/
|
||||
bool ShaderTerrainMesh::do_load_heightfield() {
|
||||
|
||||
if(!_heightfield.read(_heightfield_source)) {
|
||||
shader_terrain_cat.error() << "Could not load heightfield from " << _heightfield_source << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_heightfield.get_x_size() != _heightfield.get_y_size()) {
|
||||
bool ShaderTerrainMesh::do_check_heightfield() {
|
||||
if (_heightfield_tex->get_x_size() != _heightfield_tex->get_y_size()) {
|
||||
shader_terrain_cat.error() << "Only square heightfields are supported!";
|
||||
return false;
|
||||
}
|
||||
|
||||
_size = _heightfield.get_x_size();
|
||||
|
||||
_size = _heightfield_tex->get_x_size();
|
||||
if (_size < 32 || !check_power_of_two(_size)) {
|
||||
shader_terrain_cat.error() << "Invalid heightfield! Needs to be >= 32 and a power of two (was: "
|
||||
<< _size << ")!" << endl;
|
||||
@ -687,8 +679,8 @@ void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) {
|
||||
data_entry.size = chunk->size / _chunk_size;
|
||||
data_entry.clod = chunk->last_clod;
|
||||
|
||||
data->emitted_chunks ++;
|
||||
data->storage_ptr ++;
|
||||
data->emitted_chunks++;
|
||||
data->storage_ptr++;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -701,7 +693,9 @@ void ShaderTerrainMesh::do_emit_chunk(Chunk* chunk, TraversalData* data) {
|
||||
* @return World-Space point
|
||||
*/
|
||||
LPoint3 ShaderTerrainMesh::uv_to_world(const LTexCoord& coord) const {
|
||||
nassertr(_heightfield_tex != NULL, LPoint3(0));
|
||||
nassertr(_heightfield_tex != NULL, LPoint3(0)); // Heightfield not set yet
|
||||
nassertr(_heightfield_tex->has_ram_image(), LPoint3(0)); // Heightfield not in memory
|
||||
|
||||
PT(TexturePeeker) peeker = _heightfield_tex->peek();
|
||||
nassertr(peeker != NULL, LPoint3(0));
|
||||
|
||||
|
@ -55,9 +55,9 @@ PUBLISHED:
|
||||
|
||||
ShaderTerrainMesh();
|
||||
|
||||
INLINE void set_heightfield_filename(const Filename& filename);
|
||||
INLINE const Filename& get_heightfield_filename() const;
|
||||
MAKE_PROPERTY(heightfield_filename, get_heightfield_filename, set_heightfield_filename);
|
||||
INLINE void set_heightfield(Texture* heightfield);
|
||||
INLINE Texture* get_heightfield() const;
|
||||
MAKE_PROPERTY(heightfield, get_heightfield, set_heightfield);
|
||||
|
||||
INLINE void set_chunk_size(size_t chunk_size);
|
||||
INLINE size_t get_chunk_size() const;
|
||||
@ -152,8 +152,8 @@ private:
|
||||
ChunkDataEntry* storage_ptr;
|
||||
};
|
||||
|
||||
bool do_load_heightfield();
|
||||
void do_convert_heightfield();
|
||||
bool do_check_heightfield();
|
||||
void do_extract_heightfield();
|
||||
void do_init_data_texture();
|
||||
void do_create_chunks();
|
||||
void do_init_chunk(Chunk* chunk);
|
||||
@ -164,7 +164,6 @@ private:
|
||||
bool do_check_lod_matches(Chunk* chunk, TraversalData* data);
|
||||
|
||||
Chunk _base_chunk;
|
||||
Filename _heightfield_source;
|
||||
size_t _size;
|
||||
size_t _chunk_size;
|
||||
bool _generate_patches;
|
||||
|
@ -2,16 +2,15 @@
|
||||
|
||||
# Author: tobspr
|
||||
#
|
||||
# Last Updated: 2016-02-13
|
||||
# Last Updated: 2016-04-30
|
||||
#
|
||||
# This tutorial provides an example of using the ShaderTerrainMesh class
|
||||
|
||||
import os, sys, math, random
|
||||
|
||||
from direct.showbase.ShowBase import ShowBase
|
||||
from panda3d.core import ShaderTerrainMesh, Shader, load_prc_file_data
|
||||
from panda3d.core import SamplerState
|
||||
|
||||
|
||||
class ShaderTerrainDemo(ShowBase):
|
||||
def __init__(self):
|
||||
|
||||
@ -19,14 +18,14 @@ class ShaderTerrainDemo(ShowBase):
|
||||
# before the ShowBase is initialized
|
||||
load_prc_file_data("", """
|
||||
textures-power-2 none
|
||||
window-title Panda3D Shader Terrain Demo
|
||||
gl-coordinate-system default
|
||||
window-title Panda3D ShaderTerrainMesh Demo
|
||||
""")
|
||||
|
||||
# Initialize the showbase
|
||||
ShowBase.__init__(self)
|
||||
|
||||
# Increase camera FOV aswell as the far plane
|
||||
# Increase camera FOV as well as the far plane
|
||||
self.camLens.set_fov(90)
|
||||
self.camLens.set_near_far(0.1, 50000)
|
||||
|
||||
@ -35,7 +34,7 @@ class ShaderTerrainDemo(ShowBase):
|
||||
|
||||
# Set a heightfield, the heightfield should be a 16-bit png and
|
||||
# have a quadratic size of a power of two.
|
||||
self.terrain_node.heightfield_filename = "heightfield.png"
|
||||
self.terrain_node.heightfield = self.loader.loadTexture("heightfield.png")
|
||||
|
||||
# Set the target triangle width. For a value of 10.0 for example,
|
||||
# the terrain will attempt to make every triangle 10 pixels wide on screen.
|
||||
@ -44,24 +43,28 @@ class ShaderTerrainDemo(ShowBase):
|
||||
# Generate the terrain
|
||||
self.terrain_node.generate()
|
||||
|
||||
# Attach the terrain to the main scene and set its scale
|
||||
# Attach the terrain to the main scene and set its scale. With no scale
|
||||
# set, the terrain ranges from (0, 0, 0) to (1, 1, 1)
|
||||
self.terrain = self.render.attach_new_node(self.terrain_node)
|
||||
self.terrain.set_scale(1024, 1024, 100)
|
||||
self.terrain.set_pos(-512, -512, -70.0)
|
||||
|
||||
# Set a shader on the terrain. The ShaderTerrainMesh only works with
|
||||
# an applied shader. You can use the shaders used here in your own shaders
|
||||
# an applied shader. You can use the shaders used here in your own application
|
||||
terrain_shader = Shader.load(Shader.SL_GLSL, "terrain.vert.glsl", "terrain.frag.glsl")
|
||||
self.terrain.set_shader(terrain_shader)
|
||||
self.terrain.set_shader_input("camera", self.camera)
|
||||
|
||||
# Shortcut to view the wireframe mesh
|
||||
self.accept("f3", self.toggleWireframe)
|
||||
|
||||
# Set some texture on the terrain
|
||||
grass_tex = self.loader.loadTexture("textures/grass.png")
|
||||
grass_tex.set_minfilter(SamplerState.FT_linear_mipmap_linear)
|
||||
grass_tex.set_anisotropic_degree(16)
|
||||
self.terrain.set_texture(grass_tex)
|
||||
|
||||
# Load some skybox - you can safely ignore this code
|
||||
# Load a skybox - you can safely ignore this code
|
||||
skybox = self.loader.loadModel("models/skybox.bam")
|
||||
skybox.reparent_to(self.render)
|
||||
skybox.set_scale(20000)
|
||||
@ -77,5 +80,4 @@ class ShaderTerrainDemo(ShowBase):
|
||||
skybox_shader = Shader.load(Shader.SL_GLSL, "skybox.vert.glsl", "skybox.frag.glsl")
|
||||
skybox.set_shader(skybox_shader)
|
||||
|
||||
demo = ShaderTerrainDemo()
|
||||
demo.run()
|
||||
ShaderTerrainDemo().run()
|
||||
|
Loading…
x
Reference in New Issue
Block a user