// Filename: xFileMaterial.cxx
// Created by:  drose (19Jun01)
//
////////////////////////////////////////////////////////////////////
//
// 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 "xFileMaterial.h"
#include "xFileToEggConverter.h"
#include "xFileDataNode.h"
#include "eggMaterial.h"
#include "eggTexture.h"
#include "eggPrimitive.h"
#include "datagram.h"

#include <string.h>  // for strcmp, strdup

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
XFileMaterial::
XFileMaterial() {
  _face_color.set(1.0, 1.0, 1.0, 1.0);
  _specular_color.set(0.0, 0.0, 0.0);
  _emissive_color.set(0.0, 0.0, 0.0);
  _power = 64.0;

  _has_material = false;
  _has_texture = false;
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::Destructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
XFileMaterial::
~XFileMaterial() {
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::set_from_egg
//       Access: Public
//  Description: Sets the structure up from the indicated egg data.
////////////////////////////////////////////////////////////////////
void XFileMaterial::
set_from_egg(EggPrimitive *egg_prim) {
  // First, determine the face color.
  if (egg_prim->has_color()) {
    _face_color = egg_prim->get_color();
    _has_material = true;
  }

  // Do we have an actual EggMaterial, to control lighting effects?
  if (egg_prim->has_material()) {
    _has_material = true;
    EggMaterial *egg_mat = egg_prim->get_material();
    if (egg_mat->has_diff()) {
      _face_color = egg_mat->get_diff();
    }
    if (egg_mat->has_spec()) {
      const Colorf &spec = egg_mat->get_spec();
      _specular_color.set(spec[0], spec[1], spec[2]);
    }
    if (egg_mat->has_emit()) {
      const Colorf &emit = egg_mat->get_emit();
      _emissive_color.set(emit[0], emit[1], emit[2]);
    }
    if (egg_mat->has_shininess()) {
      _power = egg_mat->get_shininess();
    }
  }

  // Finally, if we also have a texture, record that.
  if (egg_prim->has_texture()) {
    _has_material = true;
    _has_texture = true;
    EggTexture *egg_tex = egg_prim->get_texture();
    _texture = egg_tex->get_filename();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::apply_to_egg
//       Access: Public
//  Description: Applies the properties in the material to the
//               indicated egg primitive.
////////////////////////////////////////////////////////////////////
void XFileMaterial::
apply_to_egg(EggPrimitive *egg_prim, XFileToEggConverter *converter) {
  // Is there a texture?
  if (_has_texture) {
    Filename texture = converter->convert_model_path(_texture);
    EggTexture temp("", texture);
    EggTexture *egg_tex = converter->create_unique_texture(temp);
    egg_prim->set_texture(egg_tex);
  }

  // Are there any fancy lighting effects?
  bool got_spec = (_specular_color != RGBColorf::zero());
  bool got_emit = (_emissive_color != RGBColorf::zero());
  if (got_spec || got_emit) {
    EggMaterial temp("");
    temp.set_diff(_face_color);
    if (got_spec) {
      temp.set_shininess(_power);
      temp.set_spec(Colorf(_specular_color[0], _specular_color[1],
                           _specular_color[2], 1.0));
    }
    if (got_emit) {
      temp.set_emit(Colorf(_emissive_color[0], _emissive_color[1],
                           _emissive_color[2], 1.0));
    }
    EggMaterial *egg_mat = converter->create_unique_material(temp);
    egg_prim->set_material(egg_mat);
  }

  // Also apply the face color.
  egg_prim->set_color(_face_color);
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::compare_to
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
int XFileMaterial::
compare_to(const XFileMaterial &other) const {
  int ct;
  ct = _face_color.compare_to(other._face_color);
  if (ct == 0) {
    ct = (_power == other._power) ? 0 : ((_power < other._power) ? -1 : 1);
  }
  if (ct == 0) {
    ct = _specular_color.compare_to(other._specular_color);
  }
  if (ct == 0) {
    ct = _emissive_color.compare_to(other._emissive_color);
  }
  if (ct == 0) {
    ct = strcmp(_texture.c_str(), other._texture.c_str());
  }
  return ct;
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::has_material
//       Access: Public
//  Description: Returns true if this material represents something
//               meaningful, or false if the default material is
//               sufficient.
////////////////////////////////////////////////////////////////////
bool XFileMaterial::
has_material() const {
  return _has_material;
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::has_texture
//       Access: Public
//  Description: Returns true if this material includes a texture map,
//               false otherwise.
////////////////////////////////////////////////////////////////////
bool XFileMaterial::
has_texture() const {
  return _has_texture;
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::make_x_material
//       Access: Public
//  Description: Creates a Material object for the material list.
////////////////////////////////////////////////////////////////////
XFileDataNode *XFileMaterial::
make_x_material(XFileNode *x_meshMaterials, const string &suffix) {
  XFileDataNode *x_material = 
    x_meshMaterials->add_Material("material" + suffix,
                                  _face_color, _power,
                                  _specular_color, _emissive_color);

  if (has_texture()) {
    x_material->add_TextureFilename("texture" + suffix, _texture);
  }

  return x_material;
}

////////////////////////////////////////////////////////////////////
//     Function: XFileMaterial::fill_material
//       Access: Public
//  Description: Fills the structure based on the raw data from the
//               X file's Material object.
////////////////////////////////////////////////////////////////////
bool XFileMaterial::
fill_material(XFileDataNode *obj) {
  _face_color = LCAST(float, (*obj)["faceColor"].vec4());
  _power = (*obj)["power"].d();
  _specular_color = LCAST(float, (*obj)["specularColor"].vec3());
  _emissive_color = LCAST(float, (*obj)["emissiveColor"].vec3());
  _has_material = true;

  // Walk through the children of the material.  If there are any,
  // there should be only one, and it should be just a Texture.
  int num_objects = obj->get_num_objects();
  for (int i = 0; i < num_objects; i++) {
    XFileDataNode *child = obj->get_object(i);
    if (child->is_standard_object("TextureFilename")) {
      _texture = Filename::from_os_specific((*child)["filename"].s());
      _has_texture = true;

    } else {
      if (xfile_cat.is_debug()) {
        xfile_cat.debug()
          << "Ignoring material object of unknown type: "
          << child->get_template_name() << "\n";
      }
    }
  }

  return true;
}
