/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard, * which is licensed under the MIT license (see LICENSE). * It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later). */ #include "graphics/tinygl/zgl.h" namespace TinyGL { void GLContext::glopMaterial(GLParam *p) { int mode = p[1].i; int type = p[2].i; Vector4 v(p[3].f, p[4].f, p[5].f, p[6].f); GLMaterial *m; if (mode == TGL_FRONT_AND_BACK) { p[1].i = TGL_FRONT; glopMaterial(p); mode = TGL_BACK; } if (mode == TGL_FRONT) m = &materials[0]; else m = &materials[1]; switch (type) { case TGL_EMISSION: m->emission = v; break; case TGL_AMBIENT: m->ambient = v; break; case TGL_DIFFUSE: m->diffuse = v; break; case TGL_SPECULAR: m->specular = v; m->has_specular = v.X != 0 || v.Y != 0 || v.Z != 0 || v.W != 1; break; case TGL_SHININESS: m->shininess = v.X; m->shininess_i = (int)(v.X / 128.0f) * SPECULAR_BUFFER_RESOLUTION; break; case TGL_AMBIENT_AND_DIFFUSE: m->diffuse = v; m->ambient = v; break; default: assert(0); } } void GLContext::glopColorMaterial(GLParam *p) { int mode = p[1].i; int type = p[2].i; current_color_material_mode = mode; current_color_material_type = type; } void GLContext::glopLight(GLParam *p) { int light = p[1].i; int type = p[2].i; Vector4 v(p[3].f, p[4].f, p[5].f, p[6].f); GLLight *l; assert(light >= TGL_LIGHT0 && light < TGL_LIGHT0 + T_MAX_LIGHTS); l = &lights[light - TGL_LIGHT0]; switch (type) { case TGL_AMBIENT: l->ambient = v; break; case TGL_DIFFUSE: l->diffuse = v; break; case TGL_SPECULAR: l->specular = v; l->has_specular = v.X != 0 || v.Y != 0 || v.Z != 0 || v.W != 1; break; case TGL_POSITION: { Vector4 pos; matrix_stack_ptr[0]->transform(v, pos); l->position = pos; if (l->position.W == 0) { l->norm_position.X = pos.X; l->norm_position.Y = pos.Y; l->norm_position.Z = pos.Z; l->norm_position.normalize(); } } break; case TGL_SPOT_DIRECTION: l->spot_direction.X = v.X; l->spot_direction.Y = v.Y; l->spot_direction.Z = v.Z; matrix_stack_ptr[0]->transform3x3(l->spot_direction, l->norm_spot_direction); l->norm_spot_direction.normalize(); break; case TGL_SPOT_EXPONENT: l->spot_exponent = v.X; break; case TGL_SPOT_CUTOFF: { float a = v.X; assert(a == 180 || (a >= 0 && a <= 90)); l->spot_cutoff = a; if (a != 180) l->cos_spot_cutoff = (float)(cos(a * (float)M_PI / 180.0)); } break; case TGL_CONSTANT_ATTENUATION: l->attenuation[0] = v.X; break; case TGL_LINEAR_ATTENUATION: l->attenuation[1] = v.X; break; case TGL_QUADRATIC_ATTENUATION: l->attenuation[2] = v.X; break; default: assert(0); } } void GLContext::glopLightModel(GLParam *p) { int pname = p[1].i; switch (pname) { case TGL_LIGHT_MODEL_AMBIENT: ambient_light_model = Vector4(p[2].f, p[3].f, p[4].f, p[5].f); break; case TGL_LIGHT_MODEL_LOCAL_VIEWER: local_light_model = (int)p[2].f; break; case TGL_LIGHT_MODEL_TWO_SIDE: light_model_two_side = (int)p[2].f; break; default: warning("glopLightModel: illegal pname: 0x%x", pname); break; } } void GLContext::gl_enable_disable_light(int light, int v) { GLLight *l = &lights[light]; if (v && !l->enabled) { l->enabled = 1; if (first_light != l) { l->next = first_light; if (first_light) first_light->prev = l; first_light = l; l->prev = nullptr; } } else if (!v && l->enabled) { l->enabled = 0; if (!l->prev) first_light = l->next; else l->prev->next = l->next; if (l->next) l->next->prev = l->prev; } } // non optimized lightening model void GLContext::gl_shade_vertex(GLVertex *v) { float R, G, B, A; GLMaterial *m; GLLight *l; Vector3 n, s, d; float dist, tmp, att, dot, dot_spot, dot_spec; int twoside = light_model_two_side; m = &materials[0]; n = v->normal; R = m->emission.X + m->ambient.X * ambient_light_model.X; G = m->emission.Y + m->ambient.Y * ambient_light_model.Y; B = m->emission.Z + m->ambient.Z * ambient_light_model.Z; A = clampf(m->diffuse.W, 0, 1); for (l = first_light; l != nullptr; l = l->next) { float lR, lB, lG; // ambient lR = l->ambient.X * m->ambient.X; lG = l->ambient.Y * m->ambient.Y; lB = l->ambient.Z * m->ambient.Z; if (l->position.W == 0) { // light at infinity d.X = l->norm_position.X; d.Y = l->norm_position.Y; d.Z = l->norm_position.Z; dist = 1; att = 1; } else { // distance attenuation d.X = l->position.X - v->ec.X; d.Y = l->position.Y - v->ec.Y; d.Z = l->position.Z - v->ec.Z; dist = sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z); att = 1.0f / (l->attenuation[0] + dist * (l->attenuation[1] + dist * l->attenuation[2])); } dot = d.X * n.X + d.Y * n.Y + d.Z * n.Z; if (twoside && dot < 0) dot = -dot; if (dot > 0) { tmp = 1 / dist; d *= tmp; dot *= tmp; // diffuse light lR += dot * l->diffuse.X * m->diffuse.X; lG += dot * l->diffuse.Y * m->diffuse.Y; lB += dot * l->diffuse.Z * m->diffuse.Z; const bool is_spotlight = l->spot_cutoff != 180; const bool has_specular = l->has_specular && m->has_specular; if (is_spotlight || has_specular) { if (is_spotlight) { dot_spot = -(d.X * l->norm_spot_direction.X + d.Y * l->norm_spot_direction.Y + d.Z * l->norm_spot_direction.Z); if (twoside && dot_spot < 0) dot_spot = -dot_spot; if (dot_spot < l->cos_spot_cutoff) { // no contribution continue; } else { // TODO: optimize if (l->spot_exponent > 0) { att = att * pow(dot_spot, l->spot_exponent); } } } if (has_specular) { if (local_light_model) { Vector3 vcoord; vcoord.X = v->ec.X; vcoord.Y = v->ec.Y; vcoord.Z = v->ec.Z; vcoord.normalize(); s.X = d.X - vcoord.X; s.Y = d.Y - vcoord.Y; s.Z = d.Z - vcoord.Z; } else { s.X = d.X; s.Y = d.Y; s.Z = (float)(d.Z + 1.0); } dot_spec = n.X * s.X + n.Y * s.Y + n.Z * s.Z; if (twoside && dot_spec < 0) dot_spec = -dot_spec; if (dot_spec > 0) { GLSpecBuf *specbuf; int idx; dot_spec = dot_spec / sqrt(s.X * s.X + s.Y * s.Y + s.Z * s.Z); // TODO: optimize // testing specular buffer code // dot_spec= pow(dot_spec,m->shininess) specbuf = specbuf_get_buffer(m->shininess_i, m->shininess); tmp = dot_spec * SPECULAR_BUFFER_SIZE; if (tmp > SPECULAR_BUFFER_SIZE) idx = SPECULAR_BUFFER_SIZE; else idx = (int)tmp; dot_spec = specbuf->buf[idx]; lR += dot_spec * l->specular.X * m->specular.X; lG += dot_spec * l->specular.Y * m->specular.Y; lB += dot_spec * l->specular.Z * m->specular.Z; } } } } R += att * lR; G += att * lG; B += att * lB; } v->color.X = clampf(current_color.X * R, 0, 1); v->color.Y = clampf(current_color.Y * G, 0, 1); v->color.Z = clampf(current_color.Z * B, 0, 1); v->color.W = current_color.W * A; } } // end of namespace TinyGL