377 lines
14 KiB
C++
377 lines
14 KiB
C++
// Filename: rocketRenderInterface.cxx
|
|
// Created by: rdb (04Nov11)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "rocketRenderInterface.h"
|
|
#include "cullableObject.h"
|
|
#include "cullHandler.h"
|
|
#include "geomVertexData.h"
|
|
#include "geomVertexArrayData.h"
|
|
#include "internalName.h"
|
|
#include "geomVertexWriter.h"
|
|
#include "geomTriangles.h"
|
|
#include "colorBlendAttrib.h"
|
|
#include "cullBinAttrib.h"
|
|
#include "depthTestAttrib.h"
|
|
#include "depthWriteAttrib.h"
|
|
#include "scissorAttrib.h"
|
|
#include "texture.h"
|
|
#include "textureAttrib.h"
|
|
#include "texturePool.h"
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::render
|
|
// Access: Public
|
|
// Description: Called by RocketNode in cull_callback. Invokes
|
|
// context->Render() and culls the result.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
render(Rocket::Core::Context* context, CullTraverser *trav) {
|
|
nassertv(context != NULL);
|
|
MutexHolder holder(_lock);
|
|
|
|
_trav = trav;
|
|
_net_transform = trav->get_world_transform();
|
|
_net_state = RenderState::make(
|
|
CullBinAttrib::make("unsorted", 0),
|
|
DepthTestAttrib::make(RenderAttrib::M_none),
|
|
DepthWriteAttrib::make(DepthWriteAttrib::M_off),
|
|
ColorBlendAttrib::make(ColorBlendAttrib::M_add,
|
|
ColorBlendAttrib::O_incoming_alpha,
|
|
ColorBlendAttrib::O_one_minus_incoming_alpha
|
|
)
|
|
);
|
|
_dimensions = context->GetDimensions();
|
|
|
|
context->Render();
|
|
|
|
_trav = NULL;
|
|
_net_transform = NULL;
|
|
_net_state = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::make_geom
|
|
// Access: Protected
|
|
// Description: Called internally to make a Geom from Rocket data.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(Geom) RocketRenderInterface::
|
|
make_geom(Rocket::Core::Vertex* vertices,
|
|
int num_vertices, int* indices, int num_indices,
|
|
GeomEnums::UsageHint uh, const LVecBase2 &tex_scale) {
|
|
|
|
PT(GeomVertexData) vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4t2(), uh);
|
|
vdata->unclean_set_num_rows(num_vertices);
|
|
{
|
|
GeomVertexWriter vwriter(vdata, InternalName::get_vertex());
|
|
GeomVertexWriter cwriter(vdata, InternalName::get_color());
|
|
GeomVertexWriter twriter(vdata, InternalName::get_texcoord());
|
|
|
|
// Write the vertex information.
|
|
for (int i = 0; i < num_vertices; ++i) {
|
|
const Rocket::Core::Vertex &vertex = vertices[i];
|
|
|
|
vwriter.add_data3f(LVector3f::right() * vertex.position.x + LVector3f::up() * vertex.position.y);
|
|
cwriter.add_data4i(vertex.colour.red, vertex.colour.green,
|
|
vertex.colour.blue, vertex.colour.alpha);
|
|
twriter.add_data2f(vertex.tex_coord.x * tex_scale[0],
|
|
(1.0f - vertex.tex_coord.y) * tex_scale[1]);
|
|
}
|
|
}
|
|
|
|
// Create a primitive and write the indices.
|
|
PT(GeomTriangles) triangles = new GeomTriangles(uh);
|
|
{
|
|
PT(GeomVertexArrayData) idata = triangles->modify_vertices();
|
|
idata->unclean_set_num_rows(num_indices);
|
|
GeomVertexWriter iwriter(idata, 0);
|
|
|
|
for (int i = 0; i < num_indices; ++i) {
|
|
iwriter.add_data1i(indices[i]);
|
|
}
|
|
}
|
|
|
|
PT(Geom) geom = new Geom(vdata);
|
|
geom->add_primitive(triangles);
|
|
return geom;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::render_geom
|
|
// Access: Protected
|
|
// Description: Only call this during render(). Culls a geom.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
render_geom(const Geom* geom, const RenderState* state, const Rocket::Core::Vector2f& translation) {
|
|
LVector3 offset = LVector3::right() * translation.x + LVector3::up() * translation.y;
|
|
|
|
if (_enable_scissor) {
|
|
state = state->add_attrib(ScissorAttrib::make(_scissor));
|
|
rocket_cat.spam()
|
|
<< "Rendering geom " << geom << " with state "
|
|
<< *state << ", translation (" << offset << "), "
|
|
<< "scissor region (" << _scissor << ")\n";
|
|
} else {
|
|
rocket_cat.spam()
|
|
<< "Rendering geom " << geom << " with state "
|
|
<< *state << ", translation (" << offset << ")\n";
|
|
}
|
|
|
|
CPT(TransformState) internal_transform =
|
|
_trav->get_scene()->get_cs_world_transform()->compose(
|
|
_net_transform->compose(TransformState::make_pos(offset)));
|
|
|
|
CullableObject *object =
|
|
new CullableObject(geom, _net_state->compose(state),
|
|
internal_transform);
|
|
_trav->get_cull_handler()->record_object(object, _trav);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::RenderGeometry
|
|
// Access: Protected
|
|
// Description: Called by Rocket when it wants to render geometry
|
|
// that the application does not wish to optimize.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
RenderGeometry(Rocket::Core::Vertex* vertices,
|
|
int num_vertices, int* indices, int num_indices,
|
|
Rocket::Core::TextureHandle thandle,
|
|
const Rocket::Core::Vector2f& translation) {
|
|
|
|
Texture *texture = (Texture *)thandle;
|
|
|
|
LVecBase2 tex_scale(1, 1);
|
|
if (texture != (Texture *)NULL) {
|
|
tex_scale = texture->get_tex_scale();
|
|
}
|
|
|
|
PT(Geom) geom = make_geom(vertices, num_vertices, indices, num_indices,
|
|
GeomEnums::UH_stream, tex_scale);
|
|
|
|
CPT(RenderState) state;
|
|
if (texture != (Texture *)NULL) {
|
|
state = RenderState::make(TextureAttrib::make(texture));
|
|
} else {
|
|
state = RenderState::make_empty();
|
|
}
|
|
|
|
render_geom(geom, state, translation);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::CompileGeometry
|
|
// Access: Protected
|
|
// Description: Called by Rocket when it wants to compile geometry
|
|
// it believes will be static for the forseeable future.
|
|
////////////////////////////////////////////////////////////////////
|
|
Rocket::Core::CompiledGeometryHandle RocketRenderInterface::
|
|
CompileGeometry(Rocket::Core::Vertex* vertices,
|
|
int num_vertices, int* indices, int num_indices,
|
|
Rocket::Core::TextureHandle thandle) {
|
|
|
|
Texture *texture = (Texture *)thandle;
|
|
|
|
CompiledGeometry *c = new CompiledGeometry;
|
|
LVecBase2 tex_scale(1, 1);
|
|
|
|
if (texture != (Texture *)NULL) {
|
|
rocket_cat.debug()
|
|
<< "Compiling geom " << c->_geom << " with texture '"
|
|
<< texture->get_name() << "'\n";
|
|
|
|
tex_scale = texture->get_tex_scale();
|
|
|
|
PT(TextureStage) stage = new TextureStage("");
|
|
stage->set_mode(TextureStage::M_modulate);
|
|
|
|
CPT(TextureAttrib) attr = DCAST(TextureAttrib, TextureAttrib::make());
|
|
attr = DCAST(TextureAttrib, attr->add_on_stage(stage, (Texture *)texture));
|
|
|
|
c->_state = RenderState::make(attr);
|
|
|
|
} else {
|
|
rocket_cat.debug()
|
|
<< "Compiling geom " << c->_geom << " without texture\n";
|
|
|
|
c->_state = RenderState::make_empty();
|
|
}
|
|
|
|
c->_geom = make_geom(vertices, num_vertices, indices, num_indices,
|
|
GeomEnums::UH_static, tex_scale);
|
|
|
|
return (Rocket::Core::CompiledGeometryHandle) c;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::RenderCompiledGeometry
|
|
// Access: Protected
|
|
// Description: Called by Rocket when it wants to render
|
|
// application-compiled geometry.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
RenderCompiledGeometry(Rocket::Core::CompiledGeometryHandle geometry, const Rocket::Core::Vector2f& translation) {
|
|
|
|
CompiledGeometry *c = (CompiledGeometry*) geometry;
|
|
render_geom(c->_geom, c->_state, translation);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::ReleaseCompiledGeometry
|
|
// Access: Protected
|
|
// Description: Called by Rocket when it wants to release
|
|
// application-compiled geometry.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
ReleaseCompiledGeometry(Rocket::Core::CompiledGeometryHandle geometry) {
|
|
delete (CompiledGeometry*) geometry;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::LoadTexture
|
|
// Access: Protected
|
|
// Description: Called by Rocket when a texture is required by the
|
|
// library.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RocketRenderInterface::
|
|
LoadTexture(Rocket::Core::TextureHandle& texture_handle,
|
|
Rocket::Core::Vector2i& texture_dimensions,
|
|
const Rocket::Core::String& source) {
|
|
|
|
// Prefer padding over scaling to avoid blurring people's pixel art.
|
|
LoaderOptions options;
|
|
if (Texture::get_textures_power_2() == ATS_none) {
|
|
options.set_auto_texture_scale(ATS_none);
|
|
} else {
|
|
options.set_auto_texture_scale(ATS_pad);
|
|
}
|
|
|
|
Filename fn = Filename::from_os_specific(source.CString());
|
|
PT(Texture) tex = TexturePool::load_texture(fn, 0, false, options);
|
|
if (tex == NULL) {
|
|
texture_handle = 0;
|
|
texture_dimensions.x = 0;
|
|
texture_dimensions.y = 0;
|
|
return false;
|
|
}
|
|
|
|
tex->set_minfilter(SamplerState::FT_nearest);
|
|
tex->set_magfilter(SamplerState::FT_nearest);
|
|
|
|
// Since libRocket may make layout decisions based on the size of
|
|
// the image, it's important that we give it the original size of
|
|
// the image file in order to produce consistent results.
|
|
int width = tex->get_orig_file_x_size();
|
|
int height = tex->get_orig_file_y_size();
|
|
if (width == 0 && height == 0) {
|
|
// This shouldn't happen unless someone is playing very strange
|
|
// tricks with the TexturePool, but we might as well handle it.
|
|
width = tex->get_x_size();
|
|
height = tex->get_y_size();
|
|
}
|
|
texture_dimensions.x = width;
|
|
texture_dimensions.y = height;
|
|
|
|
tex->ref();
|
|
texture_handle = (Rocket::Core::TextureHandle) tex.p();
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::GenerateTexture
|
|
// Access: Protected
|
|
// Description: Called by Rocket when a texture is required to be
|
|
// built from an internally-generated sequence of pixels.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RocketRenderInterface::
|
|
GenerateTexture(Rocket::Core::TextureHandle& texture_handle,
|
|
const Rocket::Core::byte* source,
|
|
const Rocket::Core::Vector2i& source_dimensions) {
|
|
|
|
PT(Texture) tex = new Texture;
|
|
tex->setup_2d_texture(source_dimensions.x, source_dimensions.y,
|
|
Texture::T_unsigned_byte, Texture::F_rgba);
|
|
|
|
// Pad to nearest power of two if necessary. It may not be necessary
|
|
// as libRocket seems to give power-of-two sizes already, but can't hurt.
|
|
tex->set_size_padded(source_dimensions.x, source_dimensions.y);
|
|
|
|
PTA_uchar image = tex->modify_ram_image();
|
|
|
|
// Convert RGBA to BGRA
|
|
size_t src_stride = source_dimensions.x * 4;
|
|
size_t dst_stride = tex->get_x_size() * 4;
|
|
const unsigned char *src_ptr = source + (src_stride * source_dimensions.y);
|
|
unsigned char *dst_ptr = &image[0];
|
|
|
|
for (; src_ptr > source; dst_ptr += dst_stride) {
|
|
src_ptr -= src_stride;
|
|
for (size_t i = 0; i < src_stride; i += 4) {
|
|
dst_ptr[i + 0] = src_ptr[i + 2];
|
|
dst_ptr[i + 1] = src_ptr[i + 1];
|
|
dst_ptr[i + 2] = src_ptr[i];
|
|
dst_ptr[i + 3] = src_ptr[i + 3];
|
|
}
|
|
}
|
|
|
|
tex->set_wrap_u(SamplerState::WM_clamp);
|
|
tex->set_wrap_v(SamplerState::WM_clamp);
|
|
tex->set_minfilter(SamplerState::FT_nearest);
|
|
tex->set_magfilter(SamplerState::FT_nearest);
|
|
|
|
tex->ref();
|
|
texture_handle = (Rocket::Core::TextureHandle) tex.p();
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::ReleaseTexture
|
|
// Access: Protected
|
|
// Description: Called by Rocket when a loaded texture is no longer
|
|
// required.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
ReleaseTexture(Rocket::Core::TextureHandle texture_handle) {
|
|
Texture *tex = (Texture *)texture_handle;
|
|
if (tex != (Texture *)NULL) {
|
|
unref_delete(tex);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::EnableScissorRegion
|
|
// Access: Protected
|
|
// Description: Called by Rocket when it wants to enable or disable
|
|
// scissoring to clip content.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
EnableScissorRegion(bool enable) {
|
|
_enable_scissor = enable;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RocketRenderInterface::SetScissorRegion
|
|
// Access: Protected
|
|
// Description: Called by Rocket when it wants to change the
|
|
// scissor region.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RocketRenderInterface::
|
|
SetScissorRegion(int x, int y, int width, int height) {
|
|
_scissor[0] = x / (PN_stdfloat) _dimensions.x;
|
|
_scissor[1] = (x + width) / (PN_stdfloat) _dimensions.x;
|
|
_scissor[2] = 1.0f - ((y + height) / (PN_stdfloat) _dimensions.y);
|
|
_scissor[3] = 1.0f - (y / (PN_stdfloat) _dimensions.y);
|
|
}
|