/* * DoRayMe - a quick and dirty Raytracer * Object implementation * * Created by Manoƫl Trapier * Copyright (c) 2020 986-Studio. * */ #include #include #include #include #include Shape::Shape(ShapeType type) { this->objectId = Shape::newObjectId(); this->locked = false; this->parent = nullptr; this->dropShadow = true; this->type = type; this->localTransformMatrix = Matrix4().identity(); this->materialSet = false; this->updateTransform(); } uint64_t Shape::newObjectId() { static uint64_t id = 0; uint64_t ret; ret = id++; return ret; } Tuple Shape::normalToWorld(Tuple normalVector) { Tuple world_normal = this->transposedInverseTransform * normalVector; /* W may get wrong, so hack it. This is perfectly normal as we are using a 4x4 matrix instead of a 3x3 */ world_normal.w = 0; return world_normal.normalise(); }; Tuple Shape::normalAt(Tuple point, Intersection *hit) { Tuple local_point = this->worldToObject(point); Tuple local_normal = this->localNormalAt(local_point, hit); Tuple world_normal = this->normalToWorld(local_normal); return world_normal; } void Shape::updateTransform() { if (this->locked) return; this->transformMatrix = this->localTransformMatrix; if (this->parent != nullptr) { this->transformMatrix = this->parent->transformMatrix * this->transformMatrix; } this->inverseTransform = this->transformMatrix.inverse(); this->transposedInverseTransform = this->inverseTransform.transpose(); } void Shape::setTransform(Matrix transform) { if (this->locked) return; this->localTransformMatrix = transform; this->updateTransform(); } BoundingBox Shape::getLocalBounds() { return BoundingBox(Point(-1, -1, -1), Point(1,1,1)); } BoundingBox Shape::getBounds() { BoundingBox ret; BoundingBox me = this->getLocalBounds(); ret | this->objectToWorld(Point(me.min.x, me.min.y, me.min.z)); ret | this->objectToWorld(Point(me.min.x, me.min.y, me.max.z)); ret | this->objectToWorld(Point(me.min.x, me.max.y, me.min.z)); ret | this->objectToWorld(Point(me.max.x, me.min.y, me.min.z)); ret | this->objectToWorld(Point(me.max.x, me.max.y, me.min.z)); ret | this->objectToWorld(Point(me.max.x, me.min.y, me.max.z)); ret | this->objectToWorld(Point(me.min.x, me.max.y, me.max.z)); ret | this->objectToWorld(Point(me.max.x, me.max.y, me.max.z)); return ret; } Material *Shape::getMaterial() { Shape *s = this; while((!s->materialSet) && (s->parent != nullptr)) { s = s->parent; } return &s->material; } void Shape::dumpMe(FILE *fp) { if (this->materialSet) { fprintf(fp, "\"Material\": {\n"); this->material.dumpMe(fp); fprintf(fp, "},\n"); } fprintf(fp, "\"DropShadow\": %d,\n", this->dropShadow); fprintf(fp, "\"Locked\": %d,\n", this->locked); fprintf(fp, "\"MaterialSet\": %d,\n", this->materialSet); if (this->haveFiniteBounds()) { fprintf(fp, "\"BoundingBox\": {\n"); this->getBounds().dumpMe(fp); fprintf(fp, "},\n"); } fprintf(fp, "\"id\": %ld,\n", this->getObjectId()); if (this->parent) { fprintf(fp, "\"parentId\": %ld,\n", this->parent->getObjectId()); } }